-
Notifications
You must be signed in to change notification settings - Fork 874
/
Copy pathstructure.py
150 lines (125 loc) · 5.3 KB
/
structure.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
"""This module implements symmetry-related structure forms."""
from __future__ import annotations
from typing import TYPE_CHECKING
import numpy as np
from tabulate import tabulate
from pymatgen.core.structure import PeriodicSite, Structure
if TYPE_CHECKING:
from collections.abc import Sequence
from typing_extensions import Self
from pymatgen.symmetry.analyzer import SpacegroupOperations
class SymmetrizedStructure(Structure):
"""This class represents a symmetrized structure, i.e. a structure
where the spacegroup and symmetry operations are defined. This class is
typically not called but instead is typically obtained by calling
pymatgen.symmetry.analyzer.SpacegroupAnalyzer.get_symmetrized_structure.
Attributes:
equivalent_indices (list[List[int]]): A list of lists of indices of the sites in the structure that are
considered equivalent based on the symmetry operations of the space group.
"""
def __init__(
self,
structure: Structure,
spacegroup: SpacegroupOperations,
equivalent_positions: Sequence[int],
wyckoff_letters: Sequence[str],
) -> None:
"""
Args:
structure (Structure): Original structure
spacegroup (SpacegroupOperations): An input SpacegroupOperations from SpacegroupAnalyzer.
equivalent_positions (list[int]): Equivalent positions from SpacegroupAnalyzer.
wyckoff_letters (list[str]): Wyckoff letters.
"""
self.spacegroup = spacegroup
uniq, inverse = np.unique(equivalent_positions, return_inverse=True)
self.site_labels = equivalent_positions
super().__init__(
structure.lattice,
[site.species for site in structure],
structure.frac_coords,
site_properties=structure.site_properties,
properties=structure.properties,
labels=structure.labels,
)
equivalent_indices: list[list[int]] = [[] for _ in range(len(uniq))]
equivalent_sites: list[list[PeriodicSite]] = [[] for _ in range(len(uniq))]
wyckoff_symbols: list[list[str]] = [[] for _ in range(len(uniq))]
for idx, inv_ in enumerate(inverse):
equivalent_indices[inv_].append(idx)
equivalent_sites[inv_].append(self.sites[idx])
wyckoff_symbols[inv_].append(wyckoff_letters[idx])
self.equivalent_indices = equivalent_indices
self.equivalent_sites = equivalent_sites
self.wyckoff_letters = wyckoff_letters
self.wyckoff_symbols = [f"{len(symb)}{symb[0]}" for symb in wyckoff_symbols]
def copy(self) -> Self:
"""Make a copy of the SymmetrizedStructure."""
return type(self)(
self,
spacegroup=self.spacegroup,
equivalent_positions=self.site_labels,
wyckoff_letters=self.wyckoff_letters,
)
def find_equivalent_sites(self, site: PeriodicSite) -> list[PeriodicSite]:
"""Find all symmetrically equivalent sites for a particular site.
Args:
site (PeriodicSite): A site in the structure
Raises:
ValueError: if site is not in the structure.
Returns:
list[PeriodicSite]: all symmetrically equivalent sites.
"""
for sites in self.equivalent_sites:
if site in sites:
return sites
raise ValueError("Site not in structure")
def __repr__(self) -> str:
return str(self)
def __str__(self) -> str:
outs = [
"SymmetrizedStructure",
f"Full Formula ({self.formula})",
f"Reduced Formula: {self.reduced_formula}",
f"Spacegroup: {self.spacegroup.int_symbol} ({self.spacegroup.int_number})",
f"abc : {' '.join(f'{val:>10.6f}' for val in self.lattice.abc)}",
f"angles: {' '.join(f'{val:>10.6f}' for val in self.lattice.angles)}",
]
if self._charge:
outs.append(f"Overall Charge: {self._charge:+}")
outs.append(f"Sites ({len(self)})")
data = []
props = self.site_properties
keys = sorted(props)
for idx, sites in enumerate(self.equivalent_sites):
site = sites[0]
row = [str(idx), site.species_string]
row.extend([f"{j:>10.6f}" for j in site.frac_coords])
row.append(self.wyckoff_symbols[idx])
row += [props[key][idx] for key in keys]
data.append(row)
outs.append(tabulate(data, headers=["#", "SP", "a", "b", "c", "Wyckoff", *keys]))
return "\n".join(outs)
def as_dict(self):
"""MSONable dict."""
structure = Structure.from_sites(self.sites)
return {
"structure": structure.as_dict(),
"spacegroup": self.spacegroup,
"equivalent_positions": self.site_labels,
"wyckoff_letters": self.wyckoff_letters,
}
@classmethod
def from_dict(cls, dct: dict) -> Self:
"""
Args:
dct (dict): Dict representation.
Returns:
SymmetrizedStructure
"""
return cls(
Structure.from_dict(dct["structure"]),
spacegroup=dct["spacegroup"],
equivalent_positions=dct["equivalent_positions"],
wyckoff_letters=dct["wyckoff_letters"],
)