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

Electrode Workflow #655

Merged
merged 57 commits into from
Feb 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
70b29b1
start electrodes
jmmshn Dec 20, 2023
25cfc9a
start electrode
jmmshn Dec 20, 2023
6cec20e
start electrode
jmmshn Dec 20, 2023
9b66d67
start electrode
jmmshn Dec 20, 2023
68aab1a
start electrode
jmmshn Dec 20, 2023
3a30efa
start electrode
jmmshn Dec 20, 2023
5921016
VASP electrode job
jmmshn Dec 20, 2023
5495c87
VASP electrode job
jmmshn Dec 20, 2023
98b9b29
lint
jmmshn Dec 20, 2023
df07747
n steps
jmmshn Dec 20, 2023
638d784
n steps
jmmshn Dec 20, 2023
5320449
n steps
jmmshn Dec 20, 2023
1862501
n steps
jmmshn Dec 20, 2023
b9825f3
rm defect changes
jmmshn Dec 20, 2023
982463d
rm defect changes
jmmshn Dec 20, 2023
8f7c7bf
update
jmmshn Dec 20, 2023
5f1316e
update
jmmshn Dec 20, 2023
f60c5ad
update
jmmshn Dec 20, 2023
db76d90
update structure matcher
jmmshn Dec 21, 2023
f084145
debugging
jmmshn Dec 22, 2023
8b44900
debugging
jmmshn Dec 22, 2023
27d6944
debugging
jmmshn Dec 22, 2023
aca956c
debugging
jmmshn Dec 22, 2023
c098638
debugging
jmmshn Dec 22, 2023
76afa32
append names
jmmshn Jan 2, 2024
5b65a4a
append names
jmmshn Jan 2, 2024
2ac0fb2
append names
jmmshn Jan 2, 2024
d2f1643
append names
jmmshn Jan 2, 2024
0e1a069
append names
jmmshn Jan 2, 2024
e96652f
dev script change
jmmshn Jan 3, 2024
9cc4705
working test
jmmshn Jan 4, 2024
0551925
Merge remote-tracking branch 'mp/main' into js_runs
jmmshn Jan 4, 2024
2a7b36d
typo
jmmshn Jan 4, 2024
217b7a1
lint
jmmshn Jan 4, 2024
25cc070
lint
jmmshn Jan 4, 2024
140b973
lint
jmmshn Jan 4, 2024
d9b73f2
lint
jmmshn Jan 4, 2024
098722f
allow different bulk relax
jmmshn Jan 5, 2024
de48025
update
jmmshn Jan 9, 2024
f31781c
update
jmmshn Jan 9, 2024
0b53ecb
update
jmmshn Jan 10, 2024
5bd1726
update
jmmshn Jan 10, 2024
793fdf1
hydrogen
jmmshn Jan 11, 2024
f366294
Merge remote-tracking branch 'mp/main' into js_runs
jmmshn Jan 11, 2024
7e38d37
update emmet
jmmshn Jan 11, 2024
50e6b97
Merge branch 'main' into electrode
jmmshn Jan 11, 2024
934b04d
ulid tests
jmmshn Jan 23, 2024
515091b
lint
jmmshn Jan 23, 2024
c7fbc94
emmet
jmmshn Jan 23, 2024
c6300b5
Merge branch 'main' into electrode
utf Feb 24, 2024
80ae1fb
Merge remote-tracking branch 'mp/main' into electrode
jmmshn Feb 24, 2024
823c92d
update ulid
jmmshn Feb 26, 2024
bdcc97d
update ulid
jmmshn Feb 26, 2024
1b0e5db
update ulid
jmmshn Feb 26, 2024
842070d
Merge remote-tracking branch 'mp/main' into electrode
jmmshn Feb 26, 2024
1d9e1bd
Merge branch 'main' into electrode
utf Feb 28, 2024
f16c016
Update pyproject.toml
utf Feb 29, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ cclib = ["cclib"]
mp = ["mp-api>=0.37.5"]
phonons = ["phonopy>=1.10.8", "seekpath"]
lobster = ["ijson>=3.2.2", "lobsterpy>=0.3.7"]
defects = ["dscribe>=1.2.0", "pymatgen-analysis-defects>=2022.11.30"]
defects = ["dscribe>=1.2.0", "pymatgen-analysis-defects>=2022.11.30", "python-ulid"]
forcefields = [
"ase>=3.22.1",
"chgnet>=0.2.2",
Expand Down Expand Up @@ -92,6 +92,7 @@ strict = [
"quippy-ase==0.9.14",
"seekpath==2.1.0",
"typing-extensions==4.10.0",
"python-ulid==2.2.0"
]

[project.scripts]
Expand Down
23 changes: 21 additions & 2 deletions src/atomate2/cli/dev.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,31 @@


@dev.command(context_settings={"help_option_names": ["-h", "--help"]})
@click.argument("test_dir")
def vasp_test_data(test_dir: Path) -> None:
@click.argument(

Check warning on line 20 in src/atomate2/cli/dev.py

View check run for this annotation

Codecov / codecov/patch

src/atomate2/cli/dev.py#L20

Added line #L20 was not covered by tests
"test_dir",
)
@click.option(

Check warning on line 23 in src/atomate2/cli/dev.py

View check run for this annotation

Codecov / codecov/patch

src/atomate2/cli/dev.py#L23

Added line #L23 was not covered by tests
"--additional_file",
"-a",
multiple=True,
help="list of additional files to copy from each completed VASP directory. "
"Example: `--additional_file CHGCAR --additional_file LOCPOT`",
)
def vasp_test_data(test_dir: str | Path, additional_file: list[str]) -> None:

Check warning on line 30 in src/atomate2/cli/dev.py

View check run for this annotation

Codecov / codecov/patch

src/atomate2/cli/dev.py#L30

Added line #L30 was not covered by tests
"""Generate test data for VASP unit tests.

This script expects there is an outputs.json file and job folders in the current
directory. Please refer to the atomate2 documentation on writing unit tests for more
information.

Parameters
----------
test_dir
The directory to write the test data to.
Should not contain spaces or punctuation.
additional_files
list of additional files to copy from each completed VASP directory.
Example: `--additional_file CHGCAR --additional_file LOCPOT`,
"""
import warnings
from pathlib import Path
Expand Down Expand Up @@ -110,6 +128,7 @@
"vasprun*",
"OUTCAR*",
"*.json*",
*additional_file,
],
allow_missing=True,
)
Expand Down
183 changes: 183 additions & 0 deletions src/atomate2/common/flows/electrode.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
"""Flow for electrode analysis."""

from __future__ import annotations

import logging
from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from typing import TYPE_CHECKING

from jobflow import Flow, Maker
from pymatgen.analysis.structure_matcher import ElementComparator, StructureMatcher

from atomate2.common.jobs.electrode import (
RelaxJobSummary,
get_computed_entries,
get_insertion_electrode_doc,
get_stable_inserted_results,
get_structure_group_doc,
)

if TYPE_CHECKING:
from pathlib import Path

Check warning on line 22 in src/atomate2/common/flows/electrode.py

View check run for this annotation

Codecov / codecov/patch

src/atomate2/common/flows/electrode.py#L22

Added line #L22 was not covered by tests

from pymatgen.alchemy import ElementLike
from pymatgen.core.structure import Structure
from pymatgen.entries.computed_entries import ComputedEntry
from pymatgen.io.vasp.outputs import VolumetricData

Check warning on line 27 in src/atomate2/common/flows/electrode.py

View check run for this annotation

Codecov / codecov/patch

src/atomate2/common/flows/electrode.py#L24-L27

Added lines #L24 - L27 were not covered by tests

logger = logging.getLogger(__name__)

__author__ = "Jimmy Shen"
__email__ = "jmmshn@gmail.com"


@dataclass
class ElectrodeInsertionMaker(Maker, ABC):
"""Attempt ion insertion into a structure.

The basic unit for cation insertion is:
[get_stable_inserted_structure]:
(static) -> (chgcar analysis) ->
N x (relax) -> (return best structure)

The workflow is:
[relax structure]
[get_stable_inserted_structure]
[get_stable_inserted_structure]
[get_stable_inserted_structure]
... until the insertion is no longer topotactic.

This workflow requires the users to provide the following functions:
self.get_charge_density(task_doc: TaskDoc):
Get the charge density of a TaskDoc output from a calculation.
self.update_static_maker():
Ensure that the static maker will store the desired data.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a paper or anything that documents this further?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah link is here, I'll add it to the PR.

https://www.nature.com/articles/s41524-020-00422-3


If you use this workflow please cite the following paper:
Shen, J.-X., Horton, M., & Persson, K. A. (2020).
A charge-density-based general cation insertion algorithm for
generating new Li-ion cathode materials.
npj Computational Materials, 6(161), 1—7.
doi: 10.1038/s41524-020-00422-3

Attributes
----------
name: str
The name of the flow created by this maker.
relax_maker: RelaxMaker
A maker to perform relaxation calculations.
bulk_relax_maker: Maker
A separate maker to perform the first bulk relaxation calculation.
If None, the relax_maker will be used.
static_maker: Maker
A maker to perform static calculations.
structure_matcher: StructureMatcher
The structure matcher to use to determine if additional insertion is needed.
"""

relax_maker: Maker
static_maker: Maker
bulk_relax_maker: Maker | None = None
name: str = "ion insertion"
structure_matcher: StructureMatcher = field(
default_factory=lambda: StructureMatcher(
comparator=ElementComparator(),
)
)

def __post_init__(self) -> None:
"""Ensure that the static maker will store the desired data."""
self.update_static_maker()

def make(
self,
structure: Structure,
inserted_element: ElementLike,
n_steps: int | None,
insertions_per_step: int = 4,
working_ion_entry: ComputedEntry | None = None,
) -> Flow:
"""Make the flow.

Parameters
----------
structure:
Structure to insert ion into.
inserted_species:
Species to insert.
n_steps: int
The maximum number of sequential insertion steps to attempt.
insertions_per_step: int
The maximum number of ion insertion sites to attempt.

Returns
-------
Flow for ion insertion.
"""
# First relax the structure
if self.bulk_relax_maker:
relax = self.bulk_relax_maker.make(structure)

Check warning on line 120 in src/atomate2/common/flows/electrode.py

View check run for this annotation

Codecov / codecov/patch

src/atomate2/common/flows/electrode.py#L120

Added line #L120 was not covered by tests
else:
relax = self.relax_maker.make(structure)
# add ignored_species to the structure matcher
sm = _add_ignored_species(self.structure_matcher, inserted_element)
# Get the inserted structure
new_entries_job = get_stable_inserted_results(
structure=relax.output.structure,
inserted_element=inserted_element,
structure_matcher=sm,
static_maker=self.static_maker,
relax_maker=self.relax_maker,
get_charge_density=self.get_charge_density,
n_steps=n_steps,
insertions_per_step=insertions_per_step,
)
relaxed_summary = RelaxJobSummary(
structure=relax.output.structure,
entry=relax.output.entry,
dir_name=relax.output.dir_name,
uuid=relax.output.uuid,
)
get_entries_job = get_computed_entries(new_entries_job.output, relaxed_summary)
structure_group_job = get_structure_group_doc(
get_entries_job.output, ignored_species=str(inserted_element)
)
jobs = [relax, new_entries_job, get_entries_job, structure_group_job]
output = structure_group_job.output
if working_ion_entry:
insertion_electrode_job = get_insertion_electrode_doc(
get_entries_job.output, working_ion_entry
)
jobs.append(insertion_electrode_job)
output = insertion_electrode_job.output
return Flow(jobs=jobs, output=output)

@abstractmethod
def get_charge_density(self, prev_dir: Path | str) -> VolumetricData:
"""Get the charge density of a structure.

Parameters
----------
prev_dir:
The previous directory where the static calculation was performed.

Returns
-------
The charge density.
"""

@abstractmethod
def update_static_maker(self) -> None:
"""Ensure that the static maker will store the desired data."""


def _add_ignored_species(
structure_matcher: StructureMatcher, species: ElementLike
) -> StructureMatcher:
"""Add an ignored species to a structure matcher."""
sm_dict = structure_matcher.as_dict()
ignored_species = set(sm_dict.get("ignored_species", set()))
ignored_species.add(str(species))
sm_dict["ignored_species"] = list(ignored_species)
return StructureMatcher.from_dict(sm_dict)
Loading
Loading