-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathcrystal.py
More file actions
205 lines (153 loc) · 7.22 KB
/
crystal.py
File metadata and controls
205 lines (153 loc) · 7.22 KB
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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
# standard library imports
from typing import Union, Any
import unittest as ut
# third-party imports
import numpy as np
import pandas as pd
from pandas import DataFrame
from ase.visualize import view
from pymatgen.analysis.local_env import CrystalNN
from pymatgen.analysis.structure_matcher import StructureMatcher as structure_matcher
from pymatgen.core.composition import Composition
from pymatgen.core.structure import Structure
from pymatgen.ext.matproj import MPRester
from pymatgen.io.ase import AseAtomsAdaptor
# local imports
import pymatgen.core.structure
class Crystal:
"""
A class representing a crystal structure.
Attributes:
structure (Structure): The pymatgen Structure object representing the crystal structure.
unique_oxygens (dict): A dictionary of unique oxygen sites in the crystal structure.
nonO (dict): A dictionary of non-oxygen sites in the crystal structure.
Methods:
__init__(self, structure: Structure): Initializes a new Crystal object with the given pymatgen Structure object.
visualize(self): Opens a visualization window of the crystal structure using ASE's view function.
unique_oxygen(self, structure: Structure): Identifies and stores the unique oxygen sites in the crystal
structure as a dictionary of pymatgen Structure objects.
non_oxygen(self, structure: Structure): Identifies and stores the non-oxygen sites in the crystal structure
as a dictionary of pymatgen Site objects.
"""
def __init__(self, structure: Structure):
"""
Initializes a new Crystal object with the given pymatgen Structure object.
Args:
structure (Structure): The pymatgen Structure object representing the crystal structure.
"""
self.structure = structure
def visualize(self):
"""
Opens a visualization window of the crystal structure using ASE's view function.
Args:
none
"""
atoms = AseAtomsAdaptor.get_atoms(self.structure)
view(atoms)
def unique_oxygen(self, structure):
"""
Identifies and stores the unique oxygen sites in the crystal structure as a dictionary of pymatgen Structure objects.
Args:
structure (Structure): The pymatgen Structure object representing the crystal structure.
"""
structure.add_oxidation_state_by_guess()
iO = []
vac_structures = {}
unique_oxygens = {}
for i, site in enumerate(structure.sites):
if site.specie.symbol == 'O':
iO.append(i)
for i in iO:
new_struct = structure.copy()
new_struct.remove_sites([i])
vac_structures[i] = new_struct
for i, vac_structure in vac_structures.items():
if i == 0:
unique_oxygens[i] = vac_structure
n_dupl = 0
for j, unique_structure in unique_oxygens.items():
if structure_matcher.fit(vac_structure, unique_structure) == True:
n_dupl += 1
if n_dupl == 0:
unique_oxygens[i] = vac_structure
self.unique_oxygens = unique_oxygens
def non_oxygen(self, structure):
"""
Identifies and stores the non-oxygen sites in the crystal structure as a dictionary of pymatgen Site objects.
Args:
structure (Structure): The pymatgen Structure object representing the crystal structure.
"""
nonO = {}
for i, site in enumerate(structure.sites):
if site.specie.symbol != 'O':
nonO[i] = site
self.nonO = nonO
def file_readin(filepath: str, visualized: bool = False) -> Union[Crystal, Any]:
"""
Reads a crystal structure from a file and returns a `Crystal` object.
Args:
filepath (str): The path to the file containing the crystal structure.
visualized (bool, optional): Whether to visualize the crystal structure using ASE. Defaults to False.
Returns:
Crystal or Any: The `Crystal` object representing the crystal structure, or `None` if the file cannot be read.
"""
structure = pymatgen.core.Structure.from_file(filepath)
crystal = Crystal(structure)
if visualized:
crystal.visualize()
return(crystal)
def oxygen_cn(crystal: Crystal) -> dict:
"""
Computes the coordination numbers and charges of the unique oxygen atoms in a crystal structure.
Args:
crystal (Crystal): The Crystal object to analyze.
Returns:
dict: A dictionary where the keys are the indices of the unique oxygen atoms in the
structure, and the values are dictionaries containing information on the coordination
numbers and charges of the atom's nearest neighbors.
"""
structure = crystal.structure
unique_oxygens = crystal.unique_oxygens
struc_NN = {}
for i in range(len(unique_oxygens.keys())):
pos_arg = list(unique_oxygens.keys())[i]
struc_NN[i] = CrystalNN(weighted_cn=True, cation_anion=True, porous_adjustment=False,
distance_cutoffs=None, x_diff_weight=0).get_nn_info(structure, pos_arg)
return(struc_NN)
def non_oxygen_oxi_state(crystal: Crystal) -> dict:
"""
Computes the oxidation states of the non-oxygen atoms in a crystal structure.
Args:
crystal (Crystal): The Crystal object to analyze.
Returns:
dict: A dictionary where the keys are the indices of the non-oxygen atoms in the
structure, and the values are the oxidation states of the atoms.
"""
structure = crystal.structure
nonO = crystal.nonO
nonO_oxi_state = {}
for i in nonO:
nonO_oxi_state[i] = structure[i].specie.add_charges_from_oxi_state_guesses
return(nonO_oxi_state)
def crystal_data(crystal: Crystal) -> DataFrame:
"""
Generates a dataframe of crystal structure data, in the following format:
| Material Name | Index of Unique Oxygen | Coordination Number | Neighbor 1: charge, CN | Neighbor 2: charge, CN | etc. |
Args:
crystal (Crystal): The Crystal object to analyze.
Returns:
dataframe: A dataframe containing the crystal structure data.
"""
structure = crystal.structure
unique_oxygens = crystal.unique_oxygens
nonO = crystal.nonO
data = []
for i in range(len(unique_oxygens.keys())):
pos_arg = list(unique_oxygens.keys())[i]
data.append([structure.formula, pos_arg, oxygen_cn(crystal)[i][pos_arg]['coordination_no'], oxygen_cn(crystal)[i][pos_arg]['site'].specie.oxi_state, oxygen_cn(crystal)[i][pos_arg]['site'].specie.symbol])
for j in range(len(oxygen_cn(crystal)[i][pos_arg]['bonds'])):
data[i].append([oxygen_cn(crystal)[i][pos_arg]['bonds'][j]['site'].specie.oxi_state, oxygen_cn(crystal)[i][pos_arg]['bonds'][j]['site'].specie.symbol])
# next, put data into a pandas dataframe for easier manipulation
crystal_df = pd.DataFrame(data, columns = ['Material Name', 'Index of Unique Oxygen', 'Coordination Number', 'Oxidation State', 'Element'])
return(crystal_df)
# class TestCrystalFeatures():