Skip to content

Commit

Permalink
Merge branch 'elec_dev' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
jmmshn committed Mar 16, 2021
2 parents f72d37f + b5e2c79 commit 6503dcc
Show file tree
Hide file tree
Showing 9 changed files with 181 additions and 101 deletions.
106 changes: 39 additions & 67 deletions emmet-builders/emmet/builders/materials/electrodes.py
@@ -1,24 +1,24 @@
import operator
import math
import operator
from collections import namedtuple
from datetime import datetime
from functools import lru_cache
from itertools import groupby, chain
from itertools import chain, groupby
from pprint import pprint
from typing import Iterable, Dict, List, Any
from typing import Any, Dict, Iterable, List

from emmet.core.electrode import InsertionElectrodeDoc
from emmet.core.structure_group import StructureGroupDoc
from emmet.core.utils import jsanitize
from maggma.builders import Builder, MapBuilder
from maggma.stores import MongoStore
from monty.json import MontyEncoder
from numpy import unique
from pymatgen.core import Composition
from pymatgen.analysis.structure_matcher import StructureMatcher, ElementComparator
from pymatgen.analysis.structure_matcher import ElementComparator, StructureMatcher
from pymatgen.apps.battery.insertion_battery import InsertionElectrode
from pymatgen.core import Structure
from pymatgen.entries.computed_entries import ComputedStructureEntry
from pymatgen.core import Composition, Structure
from pymatgen.entries.computed_entries import ComputedEntry, ComputedStructureEntry

from emmet.core.electrode import InsertionElectrodeDoc
from emmet.core.structure_group import StructureGroupDoc
from emmet.core.utils import jsanitize

__author__ = "Jimmy Shen"
__email__ = "jmmshn@lbl.gov"
Expand Down Expand Up @@ -89,18 +89,19 @@ def generic_groupby(list_in, comp=operator.eq):
return list_out



class StructureGroupBuilder(Builder):
def __init__(
self,
materials: MongoStore,
sgroups: MongoStore,
working_ion: str,
query: dict = None,
ltol: float = 0.2,
stol: float = 0.3,
angle_tol: float = 5.0,
check_newer: bool = True,
**kwargs,
self,
materials: MongoStore,
sgroups: MongoStore,
working_ion: str,
query: dict = None,
ltol: float = 0.2,
stol: float = 0.3,
angle_tol: float = 5.0,
check_newer: bool = True,
**kwargs,
):
"""
Aggregate materials entries into sgroups that are topotactically similar to each other.
Expand Down Expand Up @@ -277,21 +278,18 @@ def process_item(self, item: Any) -> Any:
def _remove_targets(self, rm_ids):
self.sgroups.remove_docs({"material_id": {"$in": rm_ids}})


class InsertionElectrodeBuilder(MapBuilder):
def __init__(
self,
grouped_materials: MongoStore,
insertion_electrode: MongoStore,
thermo: MongoStore,
material: MongoStore,
query: dict = None,
**kwargs,
self,
grouped_materials: MongoStore,
insertion_electrode: MongoStore,
thermo: MongoStore,
query: dict = None,
**kwargs,
):
self.grouped_materials = grouped_materials
self.insertion_electrode = insertion_electrode
self.thermo = thermo
self.material = material
qq_ = {} if query is None else query
qq_.update({"structure_matched": True, "has_distinct_compositions": True})
super().__init__(
Expand All @@ -304,12 +302,12 @@ def __init__(
def get_items(self):
""""""

@lru_cache(None)
@lru_cache()
def get_working_ion_entry(working_ion):
with self.thermo as store:
working_ion_docs = [*store.query({"chemsys": working_ion})]
best_wion = min(
working_ion_docs, key=lambda x: x["thermo"]["energy_per_atom"]
working_ion_docs, key=lambda x: x["energy_per_atom"]
)
return best_wion

Expand All @@ -325,35 +323,23 @@ def modify_item(item):
{"material_id": {"$in": item["grouped_ids"]}},
]
},
properties=["material_id", "_sbxn", "thermo"],
)
]

with self.material as store:
material_docs = [
*store.query(
{
"$and": [
{"material_id": {"$in": item["grouped_ids"]}},
{"_sbxn": {"$in": ["core"]}},
]
},
properties=["material_id", "structure"],
properties=["material_id", "_sbxn", "thermo", "entries", "energy_type", "energy_above_hull"],
)
]

self.logger.debug(f"Found for {len(thermo_docs)} Thermo Documents.")

if len(item["ignored_species"]) != 1:
raise ValueError(
"Insertion electrode can only be defined for one working ion species"
)

working_ion_doc = get_working_ion_entry(item["ignored_species"][0])
return {
"material_id": item["material_id"],
"working_ion_doc": working_ion_doc,
"working_ion": item["ignored_species"][0],
"thermo_docs": thermo_docs,
"material_docs": material_docs,
}

yield from map(modify_item, super().get_items())
Expand All @@ -363,40 +349,26 @@ def unary_function(self, item):
- Add volume information to each entry to create the insertion electrode document
- Add the host structure
"""
entries = [tdoc_["thermo"]["entry"] for tdoc_ in item["thermo_docs"]]
entries = list(map(ComputedEntry.from_dict, entries))
entries = [tdoc_["entries"][tdoc_["energy_type"]] for tdoc_ in item["thermo_docs"]]
entries = list(map(ComputedStructureEntry.from_dict, entries))
working_ion_entry = ComputedEntry.from_dict(
item["working_ion_doc"]["thermo"]["entry"]
item["working_ion_doc"]["entries"][item["working_ion_doc"]['energy_type']]
)
working_ion = working_ion_entry.composition.reduced_formula

decomp_energies = {
d_["material_id"]: d_["thermo"]["e_above_hull"]
d_["material_id"]: d_["energy_above_hull"]
for d_ in item["thermo_docs"]
}
mat_structures = {
mat_d_["material_id"]: Structure.from_dict(mat_d_["structure"])
for mat_d_ in item["material_docs"]
}

least_wion_ent = min(
entries, key=lambda x: x.composition.get_atomic_fraction(working_ion)
)
mdoc_ = next(
filter(
lambda x: x["material_id"] == least_wion_ent.entry_id,
item["material_docs"],
)
)
host_structure = Structure.from_dict(mdoc_["structure"])
host_structure = least_wion_ent.structure.copy()
host_structure.remove_species([item["working_ion"]])

for ient in entries:
if mat_structures[ient.entry_id].composition != ient.composition:
raise RuntimeError(
f"In {item['material_id']}: the compositions for task {ient.entry_id} are matched "
"between the StructureGroup DB and the Thermo DB "
)
ient.data["volume"] = mat_structures[ient.entry_id].volume
ient.data["volume"] = ient.structure.volume
ient.data["decomposition_energy"] = decomp_energies[ient.entry_id]

ie = InsertionElectrodeDoc.from_entries(
Expand Down
26 changes: 12 additions & 14 deletions emmet-builders/emmet/builders/vasp/thermo.py
Expand Up @@ -6,7 +6,7 @@
from maggma.core import Builder, Store
from monty.json import MontyDecoder
from pymatgen.core import Structure
from pymatgen.analysis.phase_diagram import PhaseDiagram
from pymatgen.analysis.phase_diagram import PhaseDiagramError
from pymatgen.analysis.structure_analyzer import oxide_type
from pymatgen.entries.compatibility import MaterialsProjectCompatibility
from pymatgen.entries.computed_entries import ComputedEntry, ComputedStructureEntry
Expand All @@ -18,11 +18,6 @@
)
from emmet.core.thermo import ThermoDoc
from emmet.core.vasp.calc_types import run_type
class PhaseDiagramError(Exception):
"""
An exception class for Phase Diagram generation.
"""
pass

class Thermo(Builder):
def __init__(
Expand Down Expand Up @@ -106,6 +101,8 @@ def get_items(self) -> Iterator[List[Dict]]:
def process_item(self, item: Tuple[List[str], List[ComputedEntry]]):

entries = item
if len(entries) == 0:
return []

entries = [ComputedStructureEntry.from_dict(entry) for entry in entries]
# determine chemsys
Expand All @@ -114,20 +111,21 @@ def process_item(self, item: Tuple[List[str], List[ComputedEntry]]):
)
chemsys = "-".join(elements)

self.logger.debug(f"Procesing {len(entries)} entries for {chemsys}")
self.logger.debug(f"Processing {len(entries)} entries for {chemsys}")

material_entries = defaultdict(lambda: defaultdict(list))
material_entries = defaultdict(dict)
pd_entries = []
for entry in entries:
material_entries[entry.entry_id][entry.data["run_type"]].append(entry)
material_entries[entry.entry_id][entry.data["run_type"]] = entry

# TODO: How to make this general and controllable via SETTINGS?
for material_id in material_entries:
if "GGA+U" in material_entries[material_id]:
pd_entries.extend(material_entries[material_id]["GGA+U"])
pd_entries.append(material_entries[material_id]["GGA+U"])
elif "GGA" in material_entries[material_id]:
pd_entries.extend(material_entries[material_id]["GGA"])
pd_entries.append(material_entries[material_id]["GGA"])
pd_entries = self.compatibility.process_entries(pd_entries)
self.logger.debug(f"{len(pd_entries)} remain in {chemsys} after filtering")

try:
docs = ThermoDoc.from_entries(pd_entries)
Expand All @@ -141,11 +139,11 @@ def process_item(self, item: Tuple[List[str], List[ComputedEntry]]):
elsyms.extend([el.symbol for el in e.composition.elements])

self.logger.warning(
f"Phase diagram errorin chemsys {'-'.join(sorted(set(elsyms)))}: {p}"
f"Phase diagram error in chemsys {'-'.join(sorted(set(elsyms)))}: {p}"
)
return []
except Exception as e:
self.logger.error(f"Got unexpected error: {e}")
self.logger.error(f"Got unexpected error while processing {[ent_.entry_id for ent_ in entries]}: {e}")
return []

return [d.dict() for d in docs]
Expand Down Expand Up @@ -249,7 +247,7 @@ def get_new_chemsys(self) -> Set:
thermo_mat_ids = self.thermo.distinct(self.thermo.key)
mat_ids = self.materials.distinct(self.materials.key, self.query)
dif_task_ids = list(set(mat_ids) - set(thermo_mat_ids))
q = {"task_id": {"$in": dif_task_ids}}
q = {"material_id": {"$in": dif_task_ids}}
new_mat_chemsys = set(self.materials.distinct("chemsys", q))
self.logger.debug(f"Found {len(new_mat_chemsys)} new chemical systems")

Expand Down
4 changes: 2 additions & 2 deletions emmet-builders/requirements.txt
@@ -1,3 +1,3 @@
pymatgen==2020.12.31
maggma==0.25.0
maggma>=0.25.0
-e git://github.com/materialsproject/pymatgen.git@master#egg=pymatgen
emmet-core
6 changes: 5 additions & 1 deletion emmet-builders/setup.py
@@ -1,9 +1,13 @@
import datetime
from pathlib import Path
from setuptools import setup, find_namespace_packages
required = []

with open(Path(__file__).parent / "requirements.txt") as f:
required = f.read().splitlines()
for line in f.readlines():
if "#egg=" in line:
continue
required.append(line)

setup(
name="emmet-builders",
Expand Down
7 changes: 5 additions & 2 deletions emmet-core/emmet/core/electrode.py
Expand Up @@ -117,6 +117,8 @@ class InsertionElectrodeDoc(InsertionVoltagePairDoc):

framework: Composition

electrode_object: Dict

# Make sure that the datetime field is properly formatted
@validator("last_updated", pre=True)
def last_updated_dict_ok(cls, v):
Expand All @@ -132,17 +134,18 @@ def from_entries(
) -> Union["InsertionElectrodeDoc", None]:
try:
ie = InsertionElectrode.from_entries(
entries=grouped_entries, working_ion_entry=working_ion_entry
entries=grouped_entries, working_ion_entry=working_ion_entry, strip_structures=True
)
except IndexError:
return None
d = ie.get_summary_dict()
d["num_steps"] = d.pop("nsteps", None)
d["last_updated"] = datetime.utcnow()
return cls(
task_id=task_id,
battery_id=task_id,
host_structure=host_structure.as_dict(),
framework=Composition(d["framework_formula"]),
electrode_object=ie.as_dict(),
**d
)

Expand Down

0 comments on commit 6503dcc

Please sign in to comment.