Skip to content

Commit

Permalink
Merge pull request #177 from JiwooChloeLee/master
Browse files Browse the repository at this point in the history
compare ternary/quaternary charge state fixed
  • Loading branch information
AntObi authored Oct 5, 2023
2 parents 3852284 + 4a9fa62 commit 910aba2
Show file tree
Hide file tree
Showing 3 changed files with 194 additions and 124 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ jobs:
with:
token: ${{ secrets.CODECOV_TOKEN }}
#files: ./coverage.xml
fail_ci_if_error: true
fail_ci_if_error: False
env_vars: OS,PYTHON
verbose: true

Expand Down
138 changes: 97 additions & 41 deletions examples/Dopant_Prediction/doper_example.ipynb

Large diffs are not rendered by default.

178 changes: 96 additions & 82 deletions smact/dopant_prediction/doper.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class Doper:
Methods: get_dopants, plot_dopants
Attributes:
original_species: A tuple which describes the constituent species of a material. For example:
_original_species: A tuple which describes the constituent species of a material. For example:
>>> test= Doper(("Zn2+","S2-"))
>>> test.original_species
Expand All @@ -21,23 +21,40 @@ class Doper:
"""

def __init__(
self, original_species: Tuple[str, ...], filepath: str = None
self, _original_species: Tuple[str, ...], filepath: str = None
):
"""
Intialise the `Doper` class with a tuple of species
Args:
original_species: See :class:`~.Doper`.
_original_species: See :class:`~.Doper`.
filepath (str): lambda table json file
"""
self.original_species = original_species
self._get_dopants(filepath)
self._original_species = _original_species
self._filepath = filepath

@property
def original_species(self):
return self._original_species

@original_species.setter
def original_species(self, original_species):
self._original_species = original_species

@property
def filepath(self):
return self._filepath

@filepath.setter
def filepath(self, filepath):
self._filepath = filepath

def _get_cation_dopants(
self, element_objects: List[smact.Element], cations: List[str]
):
poss_n_type_cat = []
poss_p_type_cat = []
poss_n_type_cat = set()
poss_p_type_cat = set()

for element in element_objects:
# [-2, -1, 0, +1, +2]
Expand All @@ -48,19 +65,17 @@ def _get_cation_dopants(
ele = utilities.unparse_spec((el_symbol, state))
_, charge = utilities.parse_spec(cation)
if state > charge:
if ele not in poss_n_type_cat:
poss_n_type_cat.append(ele)
poss_n_type_cat.add(ele)
elif state < charge and state > 0:
if ele not in poss_p_type_cat:
poss_p_type_cat.append(ele)
poss_p_type_cat.add(ele)

return poss_n_type_cat, poss_p_type_cat
return list(poss_n_type_cat), list(poss_p_type_cat)

def _get_anion_dopants(
self, element_objects: List[smact.Element], anions: List[str]
):
poss_n_type_an = []
poss_p_type_an = []
poss_n_type_an = set()
poss_p_type_an = set()

for element in element_objects:
oxi_state = element.oxidation_states
Expand All @@ -70,27 +85,48 @@ def _get_anion_dopants(
ele = utilities.unparse_spec((el_symbol, state))
_, charge = utilities.parse_spec(anion)
if state > charge and state < 0:
if ele not in poss_n_type_an:
poss_n_type_an.append(ele)
poss_n_type_an.add(ele)
elif state < charge:
if ele not in poss_p_type_an:
poss_p_type_an.append(ele)
return poss_n_type_an, poss_p_type_an
poss_p_type_an.add(ele)
return list(poss_n_type_an), list(poss_p_type_an)

def get_dopants(
self,
num_dopants: int = 5,
) -> dict:
"""
Args:
num_dopants (int): The number of suggestions to return for n- and p-type dopants.
Returns:
(dict): Dopant suggestions, given as a dictionary with keys
"n_type_cation", "p_type_cation", "n_type_anion", "p_type_anion".
Examples:
>>> test = Doper(('Ti4+','O2-'))
>>> print(test.get_dopants(num_dopants=2))
{'n-type cation substitutions': [('Ta5+', 8.790371775858281e-05),
('Nb5+', 7.830035204694342e-05)],
'p-type cation substitutions': [('Na1+', 0.00010060400812977031),
('Zn2+', 8.56373996146833e-05)],
'n-type anion substitutions': [('F1-', 0.01508116810515677),
('Cl1-', 0.004737202729901607)],
'p-type anion substitutions': [('N3-', 0.0014663800608945628),
('C4-', 9.31310255126729e-08)]}
"""

def _get_dopants(self, filepath: str):
cations = []
anions = []
try:
for ion in self.original_species:
for ion in self._original_species:
_, charge = utilities.parse_spec(ion)
if charge > 0:
cations.append(ion)
elif charge < 0:
anions.append(ion)
except Exception as e:
print(e, "charge is not defined")
print(f"{e}: charge is not defined for {ion}!")

CM = mutation.CationMutator.from_json(filepath)
CM = mutation.CationMutator.from_json(self._filepath)

# call all elements
element_objects = list(smact.element_dictionary().values())
Expand All @@ -104,23 +140,35 @@ def _get_dopants(self, filepath: str):

n_type_cat, p_type_cat, n_type_an, p_type_an = [], [], [], []
for cation in cations:
for n_specie, p_specie in zip(poss_n_type_cat, poss_p_type_cat):
if cation == n_specie or cation == p_specie:
cation_charge = utilities.parse_spec(cation)[1]
for n_specie in poss_n_type_cat:
n_specie_charge = utilities.parse_spec(n_specie)[1]
if cation_charge >= n_specie_charge:
continue
n_type_cat.append(
(n_specie, cation, CM.sub_prob(cation, n_specie))
)
for p_specie in poss_p_type_cat:
p_specie_charge = utilities.parse_spec(p_specie)[1]
if cation_charge <= p_specie_charge:
continue
p_type_cat.append(
(p_specie, cation, CM.sub_prob(cation, p_specie))
)

for anion in anions:
for n_specie, p_specie in zip(poss_n_type_an, poss_p_type_an):
if anion == n_specie or cation == p_specie:
anion_charge = utilities.parse_spec(anion)[1]
for n_specie in poss_n_type_an:
n_specie_charge = utilities.parse_spec(n_specie)[1]
if anion == n_specie or anion_charge >= n_specie_charge:
continue
n_type_an.append(
(n_specie, anion, CM.sub_prob(anion, n_specie))
)
for p_specie in poss_p_type_an:
p_specie_charge = utilities.parse_spec(p_specie)[1]
if anion == p_specie or anion_charge <= p_specie_charge:
continue
p_type_an.append(
(p_specie, anion, CM.sub_prob(anion, p_specie))
)
Expand All @@ -132,67 +180,33 @@ def _get_dopants(self, filepath: str):
n_type_an.sort(key=lambda x: x[-1], reverse=True)
p_type_an.sort(key=lambda x: x[-1], reverse=True)

self.n_type_cat = n_type_cat
self.p_type_cat = p_type_cat
self.n_type_an = n_type_an
self.p_type_an = p_type_an

def get_dopants(
self,
num_dopants: int = 5,
) -> dict:
"""
Args:
num_dopants (int): The number of suggestions to return for n- and p-type dopants.
Returns:
(dict): Dopant suggestions, given as a dictionary with keys
"n_type_cation", "p_type_cation", "n_type_anion", "p_type_anion".
Examples:
>>> test = Doper(('Ti4+','O2-'))
>>> print(test.get_dopants(num_dopants=2))
{'n-type cation substitutions': [('Ta5+', 8.790371775858281e-05),
('Nb5+', 7.830035204694342e-05)],
'p-type cation substitutions': [('Na1+', 0.00010060400812977031),
('Zn2+', 8.56373996146833e-05)],
'n-type anion substitutions': [('F1-', 0.01508116810515677),
('Cl1-', 0.004737202729901607)],
'p-type anion substitutions': [('N3-', 0.0014663800608945628),
('C4-', 9.31310255126729e-08)]}
"""

results = {
"n-type cation substitutions": self.n_type_cat[:num_dopants],
"p-type cation substitutions": self.p_type_cat[:num_dopants],
"n-type anion substitutions": self.n_type_an[:num_dopants],
"p-type anion substitutions": self.p_type_an[:num_dopants],
self.results = {
"n-type cation substitutions": n_type_cat[:num_dopants],
"p-type cation substitutions": p_type_cat[:num_dopants],
"n-type anion substitutions": n_type_an[:num_dopants],
"p-type anion substitutions": p_type_an[:num_dopants],
}
# return the top (num_dopants) results for each case
return results
return self.results

def plot_dopants(
self,
num_dopants: int = 5,
) -> None:
def plot_dopants(self) -> None:
"""
Uses pymatgen plotting utilities to plot the results of doping search
Args:
num_dopants (int): The number of suggestions to return for n- and p-type dopants.
None
Returns:
None
"""
results = {
"n-type cation substitutions": self.n_type_cat[:num_dopants],
"p-type cation substitutions": self.p_type_cat[:num_dopants],
"n-type anion substitutions": self.n_type_an[:num_dopants],
"p-type anion substitutions": self.p_type_an[:num_dopants],
}
for key, val in results.items():
dict_results = {utilities.parse_spec(x)[0]: y for x, _, y in val}
plotting.periodic_table_heatmap(
elemental_data=dict_results,
cmap="rainbow",
blank_color="gainsboro",
edge_color="white",
show_plot=True,
)
try:
for val in self.results.values():
dict_results = {
utilities.parse_spec(x)[0]: y for x, _, y in val
}
plotting.periodic_table_heatmap(
elemental_data=dict_results,
cmap="rainbow",
blank_color="gainsboro",
edge_color="white",
)
except AttributeError as e:
print(f"Dopants are not calculated. Run get_dopants first.")

0 comments on commit 910aba2

Please sign in to comment.