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

Minor updates #114

Merged
merged 2 commits into from
Mar 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
35 changes: 33 additions & 2 deletions pymatgen/analysis/defects/generators.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from __future__ import annotations

import collections
import itertools
import logging
from abc import ABCMeta
from itertools import combinations
Expand Down Expand Up @@ -79,8 +80,7 @@ def generate(
Returns:
Generator[Vacancy, None, None]: Generator that yields a list of ``Vacancy`` objects.
"""
all_species = [*map(str, structure.composition.elements)]

all_species = [*map(_element_str, structure.composition.elements)]
if rm_species is None:
rm_species = all_species
else:
Expand Down Expand Up @@ -397,6 +397,37 @@ def _get_candidate_sites(self, chgcar: Chgcar):
yield min(g), len(g)


def generate_all_native_defects(
host: Structure | Chgcar,
sub_generator: SubstitutionGenerator | None = None,
vac_generator: VacancyGenerator | None = None,
int_generator: ChargeInterstitialGenerator | None = None,
):
"""Generate all native defects."""
if isinstance(host, Chgcar):
struct = host.structure
chgcar = host
elif isinstance(host, Structure):
struct = host
chgcar = None
else:
raise ValueError("Host must be a Structure or Chgcar object.")

species = set(map(_element_str, struct.species))
sub_generator = sub_generator or SubstitutionGenerator()
vac_generator = vac_generator or VacancyGenerator()
# generate all vacancies
yield from vac_generator.generate(struct)
# generate substitutions for all pairs of species
for sp1, sp2 in itertools.combinations(species, 2):
yield from sub_generator.generate(struct, {sp1: sp2})
yield from sub_generator.generate(struct, {sp2: sp1})
# generate interstitials if a chgcar is provided
if chgcar is not None:
int_generator = int_generator or ChargeInterstitialGenerator()
yield from int_generator.generate(chgcar, insert_species=species)


def _element_str(sp_or_el: Species | Element) -> str:
"""Convert a species or element to a string."""
if isinstance(sp_or_el, Species):
Expand Down
23 changes: 23 additions & 0 deletions pymatgen/analysis/defects/thermo.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,12 @@ class DefectEntry(MSONable):
corrections_metadata: dict[str, Any] = field(default_factory=dict)
sc_defect_frac_coords: tuple[float, float, float] | None = None
bulk_entry: ComputedEntry | None = None
entry_id: str | None = None

def __post_init__(self) -> None:
"""Post-initialization."""
self.charge_state = int(self.charge_state)
self.entry_id = self.entry_id or self.sc_entry.entry_id

def get_freysoldt_correction(
self,
Expand Down Expand Up @@ -163,6 +165,18 @@ def get_ediff(self) -> float | None:
else:
return None

def get_summary_dict(self) -> dict:
"""Get a summary dictionary for the defect entry."""
corrections_d = {f"correction_{k}": v for k, v in self.corrections.items()}
res = {
"name": self.defect.name,
"charge_state": self.charge_state,
"bulk_total_energy": self.bulk_entry.energy if self.bulk_entry else None,
"defect_total_energy": self.sc_entry.energy,
}
res.update(corrections_d)
return res


@dataclass
class FormationEnergyDiagram(MSONable):
Expand Down Expand Up @@ -535,6 +549,15 @@ def get_concentration(
energy=fe, temperature=temperature
)

def as_dataframe(self):
"""Return the formation energy diagram as a pandas dataframe."""
from pandas import DataFrame

defect_entries = self.defect_entries
l_ = map(lambda x: x.get_summary_dict(), defect_entries)
df = DataFrame(l_)
return df


@dataclass
class MultiFormationEnergyDiagram(MSONable):
Expand Down
6 changes: 6 additions & 0 deletions tests/test_generators.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
SubstitutionGenerator,
VacancyGenerator,
VoronoiInterstitialGenerator,
generate_all_native_defects,
)


Expand Down Expand Up @@ -89,3 +90,8 @@ def test_voronoi_interstitial_generator(chgcar_fe3o4):
assert defect.site.specie.symbol == "Li"
cnt += 1
assert cnt == 4


def test_generate_all_native_defects(chgcar_fe3o4):
gen = generate_all_native_defects(chgcar_fe3o4)
len(list(gen)) == 14
3 changes: 3 additions & 0 deletions tests/test_thermo.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,9 @@ def test_formation_energy(data_Mg_Ga, defect_entries_Mg_Ga, stable_entries_Mg_Ga
temperature=300,
) == pytest.approx(2 * 1.5875937551666035e-17)

# dataframe conversion
fed.as_dataframe()


def test_multi(data_Mg_Ga, defect_entries_Mg_Ga, stable_entries_Mg_Ga_N):
bulk_vasprun = data_Mg_Ga["bulk_sc"]["vasprun"]
Expand Down