SMACT 3.1.0__tar.gz → 3.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {smact-3.1.0/SMACT.egg-info → smact-3.2.0}/PKG-INFO +5 -6
- {smact-3.1.0 → smact-3.2.0}/README.md +1 -2
- {smact-3.1.0 → smact-3.2.0/SMACT.egg-info}/PKG-INFO +5 -6
- {smact-3.1.0 → smact-3.2.0}/SMACT.egg-info/requires.txt +2 -2
- {smact-3.1.0 → smact-3.2.0}/pyproject.toml +4 -4
- {smact-3.1.0 → smact-3.2.0}/smact/data_loader.py +8 -8
- {smact-3.1.0 → smact-3.2.0}/smact/screening.py +42 -19
- {smact-3.1.0 → smact-3.2.0}/smact/tests/test_utils.py +27 -0
- {smact-3.1.0 → smact-3.2.0}/smact/utils/crystal_space/generate_composition_with_smact.py +114 -1
- {smact-3.1.0 → smact-3.2.0}/LICENSE +0 -0
- {smact-3.1.0 → smact-3.2.0}/MANIFEST.in +0 -0
- {smact-3.1.0 → smact-3.2.0}/SMACT.egg-info/SOURCES.txt +0 -0
- {smact-3.1.0 → smact-3.2.0}/SMACT.egg-info/dependency_links.txt +0 -0
- {smact-3.1.0 → smact-3.2.0}/SMACT.egg-info/top_level.txt +0 -0
- {smact-3.1.0 → smact-3.2.0}/setup.cfg +0 -0
- {smact-3.1.0 → smact-3.2.0}/smact/__init__.py +0 -0
- {smact-3.1.0 → smact-3.2.0}/smact/benchmarking/__init__.py +0 -0
- {smact-3.1.0 → smact-3.2.0}/smact/benchmarking/pymatgen_benchmark.py +0 -0
- {smact-3.1.0 → smact-3.2.0}/smact/benchmarking/smact_benchmark.py +0 -0
- {smact-3.1.0 → smact-3.2.0}/smact/benchmarking/utilities.py +0 -0
- {smact-3.1.0 → smact-3.2.0}/smact/builder.py +0 -0
- {smact-3.1.0 → smact-3.2.0}/smact/data/Covalent_radii.csv +0 -0
- {smact-3.1.0 → smact-3.2.0}/smact/data/SSE.csv +0 -0
- {smact-3.1.0 → smact-3.2.0}/smact/data/SSE_2015.csv +0 -0
- {smact-3.1.0 → smact-3.2.0}/smact/data/SSE_Pauling.csv +0 -0
- {smact-3.1.0 → smact-3.2.0}/smact/data/element_data.txt +0 -0
- {smact-3.1.0 → smact-3.2.0}/smact/data/element_valence_modified.csv +0 -0
- {smact-3.1.0 → smact-3.2.0}/smact/data/hhi.txt +0 -0
- {smact-3.1.0 → smact-3.2.0}/smact/data/ionic_radii.csv +0 -0
- {smact-3.1.0 → smact-3.2.0}/smact/data/magpie.csv +0 -0
- {smact-3.1.0 → smact-3.2.0}/smact/data/ordered_periodic.txt +0 -0
- {smact-3.1.0 → smact-3.2.0}/smact/data/oxidation_state_probability_table.json +0 -0
- {smact-3.1.0 → smact-3.2.0}/smact/data/oxidation_states.txt +0 -0
- {smact-3.1.0 → smact-3.2.0}/smact/data/oxidation_states_SP.txt +0 -0
- {smact-3.1.0 → smact-3.2.0}/smact/data/oxidation_states_icsd.txt +0 -0
- {smact-3.1.0 → smact-3.2.0}/smact/data/oxidation_states_icsd24_common.txt +0 -0
- {smact-3.1.0 → smact-3.2.0}/smact/data/oxidation_states_icsd24_counts.json +0 -0
- {smact-3.1.0 → smact-3.2.0}/smact/data/oxidation_states_icsd24_filtered.txt +0 -0
- {smact-3.1.0 → smact-3.2.0}/smact/data/oxidation_states_icsd24_raw.txt +0 -0
- {smact-3.1.0 → smact-3.2.0}/smact/data/oxidation_states_pmg.txt +0 -0
- {smact-3.1.0 → smact-3.2.0}/smact/data/oxidation_states_wiki.txt +0 -0
- {smact-3.1.0 → smact-3.2.0}/smact/data/shannon_radii.csv +0 -0
- {smact-3.1.0 → smact-3.2.0}/smact/data/shannon_radii_ML_extended.csv +0 -0
- {smact-3.1.0 → smact-3.2.0}/smact/data/solid_properties.txt +0 -0
- {smact-3.1.0 → smact-3.2.0}/smact/data/solid_properties.xlsx +0 -0
- {smact-3.1.0 → smact-3.2.0}/smact/data/species_rep/ion_embedding_M3GNet-MP-2023.11.1-oxi-Eform_cosine_similarity.json +0 -0
- {smact-3.1.0 → smact-3.2.0}/smact/data/species_rep/ion_embedding_M3GNet-MP-2023.11.1-oxi-band_gap_cosine_similarity.json +0 -0
- {smact-3.1.0 → smact-3.2.0}/smact/data/species_rep/skipspecies_20221028_319ion_dim200_cosine_similarity.json +0 -0
- {smact-3.1.0 → smact-3.2.0}/smact/distorter.py +0 -0
- {smact-3.1.0 → smact-3.2.0}/smact/dopant_prediction/__init__.py +0 -0
- {smact-3.1.0 → smact-3.2.0}/smact/dopant_prediction/doper.py +0 -0
- {smact-3.1.0 → smact-3.2.0}/smact/lattice.py +0 -0
- {smact-3.1.0 → smact-3.2.0}/smact/lattice_parameters.py +0 -0
- {smact-3.1.0 → smact-3.2.0}/smact/mainpage.py +0 -0
- {smact-3.1.0 → smact-3.2.0}/smact/metallicity.py +0 -0
- {smact-3.1.0 → smact-3.2.0}/smact/oxidation_states.py +0 -0
- {smact-3.1.0 → smact-3.2.0}/smact/properties.py +0 -0
- {smact-3.1.0 → smact-3.2.0}/smact/structure_prediction/__init__.py +0 -0
- {smact-3.1.0 → smact-3.2.0}/smact/structure_prediction/database.py +0 -0
- {smact-3.1.0 → smact-3.2.0}/smact/structure_prediction/mutation.py +0 -0
- {smact-3.1.0 → smact-3.2.0}/smact/structure_prediction/prediction.py +0 -0
- {smact-3.1.0 → smact-3.2.0}/smact/structure_prediction/probability_models.py +0 -0
- {smact-3.1.0 → smact-3.2.0}/smact/structure_prediction/structure.py +0 -0
- {smact-3.1.0 → smact-3.2.0}/smact/structure_prediction/utilities.py +0 -0
- {smact-3.1.0 → smact-3.2.0}/smact/tests/__init__.py +0 -0
- {smact-3.1.0 → smact-3.2.0}/smact/tests/test_core.py +0 -0
- {smact-3.1.0 → smact-3.2.0}/smact/tests/test_doper.py +0 -0
- {smact-3.1.0 → smact-3.2.0}/smact/tests/test_metallicity.py +0 -0
- {smact-3.1.0 → smact-3.2.0}/smact/tests/test_structure.py +0 -0
- {smact-3.1.0 → smact-3.2.0}/smact/utils/__init__.py +0 -0
- {smact-3.1.0 → smact-3.2.0}/smact/utils/band_gap_simple.py +0 -0
- {smact-3.1.0 → smact-3.2.0}/smact/utils/composition.py +0 -0
- {smact-3.1.0 → smact-3.2.0}/smact/utils/crystal_space/__init__.py +0 -0
- {smact-3.1.0 → smact-3.2.0}/smact/utils/crystal_space/download_compounds_with_mp_api.py +0 -0
- {smact-3.1.0 → smact-3.2.0}/smact/utils/crystal_space/plot_embedding.py +0 -0
- {smact-3.1.0 → smact-3.2.0}/smact/utils/oxidation.py +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: SMACT
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.2.0
|
|
4
4
|
Summary: Semiconducting Materials by Analogy and Chemical Theory
|
|
5
5
|
Author-email: The SMACT Developers <a.walsh@imperial.ac.uk>
|
|
6
6
|
Maintainer-email: "Anthony O. Onwuli" <anthony.onwuli16@imperial.ac.uk>
|
|
@@ -81,11 +81,11 @@ Requires-Dist: pandarallel>=1.6.5; extra == "optional"
|
|
|
81
81
|
Requires-Dist: matplotlib-venn[shapely]>=1.1.2; extra == "optional"
|
|
82
82
|
Provides-Extra: strict
|
|
83
83
|
Requires-Dist: pydantic==2.9.2; extra == "strict"
|
|
84
|
-
Requires-Dist: mp-api==0.45.
|
|
84
|
+
Requires-Dist: mp-api==0.45.5; extra == "strict"
|
|
85
85
|
Requires-Dist: pymatviz==0.15.1; extra == "strict"
|
|
86
|
-
Requires-Dist: mp-api==0.45.
|
|
86
|
+
Requires-Dist: mp-api==0.45.5; extra == "strict"
|
|
87
87
|
Requires-Dist: seaborn==0.13.2; extra == "strict"
|
|
88
|
-
Requires-Dist: pymatgen==2025.
|
|
88
|
+
Requires-Dist: pymatgen==2025.4.24; extra == "strict"
|
|
89
89
|
Requires-Dist: matminer==0.9.3; extra == "strict"
|
|
90
90
|
Requires-Dist: umap-learn==0.5.7; extra == "strict"
|
|
91
91
|
Requires-Dist: kaleido==0.2.1; extra == "strict"
|
|
@@ -107,7 +107,7 @@ Dynamic: license-file
|
|
|
107
107
|

|
|
108
108
|
[](https://github.com/WMD-group/SMACT/actions/workflows/ci.yml)
|
|
109
109
|
[](https://codecov.io/gh/WMD-group/SMACT)
|
|
110
|
-

|
|
110
|
+
 [](https://deepwiki.com/WMD-group/SMACT)
|
|
111
111
|
|
|
112
112
|
# SMACT
|
|
113
113
|
|
|
@@ -141,7 +141,6 @@ Use cases are available in our [examples](https://smact.readthedocs.io/en/latest
|
|
|
141
141
|
- Element compositions can be screened through based on the heuristic filters of charge neutrality and electronegativity order. This is handled using the [screening module](https://smact.readthedocs.io/en/latest/smact.screening.html) and [this publication](<https://www.cell.com/chem/fulltext/S2451-9294(16)30155-3>) describes the underlying theory. An example procedure is [outlined in the docs](https://smact.readthedocs.io/en/latest/examples/filter.html).
|
|
142
142
|
|
|
143
143
|
- Further filters can be applied to generated lists of compositions in order to screen for particular properties. These properties are either intrinsic properties of elements or are calculated for compositions using the [properties module](https://smact.readthedocs.io/en/latest/smact.properties.html). For example:
|
|
144
|
-
|
|
145
144
|
- An application is shown in [this publication](https://pubs.rsc.org/en/content/articlehtml/2018/sc/c7sc03961a), in which 160,000 chemical compositions are screened based on optical band gap calculated using the [solid-state energy scale](https://www.sciencedirect.com/science/article/pii/S0022459615300888).
|
|
146
145
|
- The [oxidation_states module](https://smact.readthedocs.io/en/latest/smact.oxidation_states.html) can be used to filter out compositions containing metals in unlikely oxidation states according to [a data-driven model](https://pubs.rsc.org/en/content/articlelanding/2018/fd/c8fd00032h#!divAbstract).
|
|
147
146
|
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|

|
|
11
11
|
[](https://github.com/WMD-group/SMACT/actions/workflows/ci.yml)
|
|
12
12
|
[](https://codecov.io/gh/WMD-group/SMACT)
|
|
13
|
-

|
|
13
|
+
 [](https://deepwiki.com/WMD-group/SMACT)
|
|
14
14
|
|
|
15
15
|
# SMACT
|
|
16
16
|
|
|
@@ -44,7 +44,6 @@ Use cases are available in our [examples](https://smact.readthedocs.io/en/latest
|
|
|
44
44
|
- Element compositions can be screened through based on the heuristic filters of charge neutrality and electronegativity order. This is handled using the [screening module](https://smact.readthedocs.io/en/latest/smact.screening.html) and [this publication](<https://www.cell.com/chem/fulltext/S2451-9294(16)30155-3>) describes the underlying theory. An example procedure is [outlined in the docs](https://smact.readthedocs.io/en/latest/examples/filter.html).
|
|
45
45
|
|
|
46
46
|
- Further filters can be applied to generated lists of compositions in order to screen for particular properties. These properties are either intrinsic properties of elements or are calculated for compositions using the [properties module](https://smact.readthedocs.io/en/latest/smact.properties.html). For example:
|
|
47
|
-
|
|
48
47
|
- An application is shown in [this publication](https://pubs.rsc.org/en/content/articlehtml/2018/sc/c7sc03961a), in which 160,000 chemical compositions are screened based on optical band gap calculated using the [solid-state energy scale](https://www.sciencedirect.com/science/article/pii/S0022459615300888).
|
|
49
48
|
- The [oxidation_states module](https://smact.readthedocs.io/en/latest/smact.oxidation_states.html) can be used to filter out compositions containing metals in unlikely oxidation states according to [a data-driven model](https://pubs.rsc.org/en/content/articlelanding/2018/fd/c8fd00032h#!divAbstract).
|
|
50
49
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: SMACT
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.2.0
|
|
4
4
|
Summary: Semiconducting Materials by Analogy and Chemical Theory
|
|
5
5
|
Author-email: The SMACT Developers <a.walsh@imperial.ac.uk>
|
|
6
6
|
Maintainer-email: "Anthony O. Onwuli" <anthony.onwuli16@imperial.ac.uk>
|
|
@@ -81,11 +81,11 @@ Requires-Dist: pandarallel>=1.6.5; extra == "optional"
|
|
|
81
81
|
Requires-Dist: matplotlib-venn[shapely]>=1.1.2; extra == "optional"
|
|
82
82
|
Provides-Extra: strict
|
|
83
83
|
Requires-Dist: pydantic==2.9.2; extra == "strict"
|
|
84
|
-
Requires-Dist: mp-api==0.45.
|
|
84
|
+
Requires-Dist: mp-api==0.45.5; extra == "strict"
|
|
85
85
|
Requires-Dist: pymatviz==0.15.1; extra == "strict"
|
|
86
|
-
Requires-Dist: mp-api==0.45.
|
|
86
|
+
Requires-Dist: mp-api==0.45.5; extra == "strict"
|
|
87
87
|
Requires-Dist: seaborn==0.13.2; extra == "strict"
|
|
88
|
-
Requires-Dist: pymatgen==2025.
|
|
88
|
+
Requires-Dist: pymatgen==2025.4.24; extra == "strict"
|
|
89
89
|
Requires-Dist: matminer==0.9.3; extra == "strict"
|
|
90
90
|
Requires-Dist: umap-learn==0.5.7; extra == "strict"
|
|
91
91
|
Requires-Dist: kaleido==0.2.1; extra == "strict"
|
|
@@ -107,7 +107,7 @@ Dynamic: license-file
|
|
|
107
107
|

|
|
108
108
|
[](https://github.com/WMD-group/SMACT/actions/workflows/ci.yml)
|
|
109
109
|
[](https://codecov.io/gh/WMD-group/SMACT)
|
|
110
|
-

|
|
110
|
+
 [](https://deepwiki.com/WMD-group/SMACT)
|
|
111
111
|
|
|
112
112
|
# SMACT
|
|
113
113
|
|
|
@@ -141,7 +141,6 @@ Use cases are available in our [examples](https://smact.readthedocs.io/en/latest
|
|
|
141
141
|
- Element compositions can be screened through based on the heuristic filters of charge neutrality and electronegativity order. This is handled using the [screening module](https://smact.readthedocs.io/en/latest/smact.screening.html) and [this publication](<https://www.cell.com/chem/fulltext/S2451-9294(16)30155-3>) describes the underlying theory. An example procedure is [outlined in the docs](https://smact.readthedocs.io/en/latest/examples/filter.html).
|
|
142
142
|
|
|
143
143
|
- Further filters can be applied to generated lists of compositions in order to screen for particular properties. These properties are either intrinsic properties of elements or are calculated for compositions using the [properties module](https://smact.readthedocs.io/en/latest/smact.properties.html). For example:
|
|
144
|
-
|
|
145
144
|
- An application is shown in [this publication](https://pubs.rsc.org/en/content/articlehtml/2018/sc/c7sc03961a), in which 160,000 chemical compositions are screened based on optical band gap calculated using the [solid-state energy scale](https://www.sciencedirect.com/science/article/pii/S0022459615300888).
|
|
146
145
|
- The [oxidation_states module](https://smact.readthedocs.io/en/latest/smact.oxidation_states.html) can be used to filter out compositions containing metals in unlikely oxidation states according to [a data-driven model](https://pubs.rsc.org/en/content/articlelanding/2018/fd/c8fd00032h#!divAbstract).
|
|
147
146
|
|
|
@@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
|
|
|
6
6
|
|
|
7
7
|
[project]
|
|
8
8
|
name = "SMACT"
|
|
9
|
-
version = "3.
|
|
9
|
+
version = "3.2.0"
|
|
10
10
|
description = "Semiconducting Materials by Analogy and Chemical Theory"
|
|
11
11
|
readme = "README.md"
|
|
12
12
|
authors = [
|
|
@@ -108,11 +108,11 @@ optional = [
|
|
|
108
108
|
]
|
|
109
109
|
strict = [
|
|
110
110
|
"pydantic==2.9.2",
|
|
111
|
-
"mp-api==0.45.
|
|
111
|
+
"mp-api==0.45.5",
|
|
112
112
|
"pymatviz==0.15.1",
|
|
113
|
-
"mp-api==0.45.
|
|
113
|
+
"mp-api==0.45.5",
|
|
114
114
|
"seaborn==0.13.2",
|
|
115
|
-
"pymatgen==2025.
|
|
115
|
+
"pymatgen==2025.4.24",
|
|
116
116
|
"matminer==0.9.3",
|
|
117
117
|
"umap-learn==0.5.7",
|
|
118
118
|
"kaleido==0.2.1",
|
|
@@ -42,11 +42,11 @@ def set_warnings(enable=True):
|
|
|
42
42
|
|
|
43
43
|
|
|
44
44
|
def _get_data_rows(filename):
|
|
45
|
-
"""Generator for datafile entries by row."""
|
|
45
|
+
"""Generator for datafile entries by row for custom oxidation states lists. Skips rows with no oxidation states for performance."""
|
|
46
46
|
with open(filename) as file:
|
|
47
47
|
for line in file:
|
|
48
48
|
line = line.strip()
|
|
49
|
-
if line[0] != "#":
|
|
49
|
+
if line[0] != "#" and any(char.isdigit() for char in line):
|
|
50
50
|
yield line.split()
|
|
51
51
|
|
|
52
52
|
|
|
@@ -58,7 +58,6 @@ def float_or_None(x):
|
|
|
58
58
|
return None
|
|
59
59
|
|
|
60
60
|
|
|
61
|
-
# Loader and cache for the element oxidation-state data.
|
|
62
61
|
_el_ox_states = None
|
|
63
62
|
|
|
64
63
|
|
|
@@ -88,7 +87,6 @@ def lookup_element_oxidation_states(symbol, copy=True):
|
|
|
88
87
|
|
|
89
88
|
if _el_ox_states is None:
|
|
90
89
|
_el_ox_states = {}
|
|
91
|
-
|
|
92
90
|
for items in _get_data_rows(os.path.join(data_directory, "oxidation_states.txt")):
|
|
93
91
|
_el_ox_states[items[0]] = [int(oxidationState) for oxidationState in items[1:]]
|
|
94
92
|
|
|
@@ -259,7 +257,7 @@ def lookup_element_oxidation_states_custom(symbol, filepath, copy=True):
|
|
|
259
257
|
|
|
260
258
|
Args:
|
|
261
259
|
----
|
|
262
|
-
symbol (str) : the atomic symbol of the element to look up.
|
|
260
|
+
symbol (str) : the atomic symbol of the element to look up. "all" can be used to return the list (copy=True) or dict (copy=False) for all oxidation states.
|
|
263
261
|
filepath (str) : the path to the text file containing the
|
|
264
262
|
oxidation states data.
|
|
265
263
|
copy (Optional(bool)): if True (default), return a copy of the
|
|
@@ -279,19 +277,21 @@ def lookup_element_oxidation_states_custom(symbol, filepath, copy=True):
|
|
|
279
277
|
|
|
280
278
|
if _el_ox_states_custom is None:
|
|
281
279
|
_el_ox_states_custom = {}
|
|
282
|
-
|
|
283
280
|
for items in _get_data_rows(filepath):
|
|
284
281
|
_el_ox_states_custom[items[0]] = [int(oxidationState) for oxidationState in items[1:]]
|
|
285
|
-
|
|
286
282
|
if symbol in _el_ox_states_custom:
|
|
287
283
|
if copy:
|
|
288
284
|
# _el_ox_states_custom stores lists -> if copy is set, make an implicit
|
|
289
285
|
# deep copy. The elements of the lists are integers, which are
|
|
290
286
|
# "value types" in Python.
|
|
291
|
-
|
|
292
287
|
return list(_el_ox_states_custom[symbol])
|
|
293
288
|
else:
|
|
294
289
|
return _el_ox_states_custom[symbol]
|
|
290
|
+
elif symbol == "all":
|
|
291
|
+
if copy:
|
|
292
|
+
return list(_el_ox_states_custom)
|
|
293
|
+
else:
|
|
294
|
+
return _el_ox_states_custom
|
|
295
295
|
else:
|
|
296
296
|
if _print_warnings:
|
|
297
297
|
print(f"WARNING: Oxidation states for element {symbol} not found.")
|
|
@@ -344,8 +344,8 @@ def smact_filter(
|
|
|
344
344
|
Returns:
|
|
345
345
|
-------
|
|
346
346
|
allowed_comps (list): Allowed compositions for that chemical system
|
|
347
|
-
in the form [(elements), (oxidation states), (ratios)] if species_unique=True
|
|
348
|
-
or in the form [(elements), (ratios)] if species_unique=False.
|
|
347
|
+
in the form [(elements), (oxidation states), (ratios)] if species_unique=True and tuple=False
|
|
348
|
+
or in the form [(elements), (ratios)] if species_unique=False and tuple=False.
|
|
349
349
|
|
|
350
350
|
Example usage:
|
|
351
351
|
>>> from smact.screening import smact_filter
|
|
@@ -371,8 +371,6 @@ def smact_filter(
|
|
|
371
371
|
|
|
372
372
|
|
|
373
373
|
"""
|
|
374
|
-
compositions = []
|
|
375
|
-
|
|
376
374
|
# Get symbols and electronegativities
|
|
377
375
|
symbols = tuple(e.symbol for e in els)
|
|
378
376
|
electronegs = [e.pauling_eneg for e in els]
|
|
@@ -385,32 +383,31 @@ def smact_filter(
|
|
|
385
383
|
"pymatgen_sp": [e.oxidation_states_sp for e in els],
|
|
386
384
|
"wiki": [e.oxidation_states_wiki for e in els],
|
|
387
385
|
}
|
|
386
|
+
|
|
388
387
|
if oxidation_states_set in oxi_set:
|
|
389
388
|
ox_combos = oxi_set[oxidation_states_set]
|
|
389
|
+
if oxidation_states_set == "wiki":
|
|
390
|
+
warnings.warn(
|
|
391
|
+
"This set of oxidation states is sourced from Wikipedia. The results from using this set could be questionable and should not be used unless you know what you are doing and have inspected the oxidation states.",
|
|
392
|
+
stacklevel=2,
|
|
393
|
+
)
|
|
390
394
|
elif os.path.exists(oxidation_states_set):
|
|
391
|
-
ox_combos =
|
|
395
|
+
ox_combos = (oxi_custom(e.symbol, oxidation_states_set) for e in els)
|
|
392
396
|
else:
|
|
393
397
|
raise (
|
|
394
398
|
Exception(
|
|
395
399
|
f'{oxidation_states_set} is not valid. Enter either "smact14", "icsd", "pymatgen","wiki" or a filepath to a textfile of oxidation states.'
|
|
396
400
|
)
|
|
397
401
|
)
|
|
398
|
-
if oxidation_states_set == "wiki":
|
|
399
|
-
warnings.warn(
|
|
400
|
-
"This set of oxidation states is sourced from Wikipedia. The results from using this set could be questionable and should not be used unless you know what you are doing and have inspected the oxidation states.",
|
|
401
|
-
stacklevel=2,
|
|
402
|
-
)
|
|
403
402
|
|
|
403
|
+
compositions = []
|
|
404
404
|
for ox_states in itertools.product(*ox_combos):
|
|
405
405
|
# Test for charge balance
|
|
406
406
|
cn_e, cn_r = neutral_ratios(ox_states, stoichs=stoichs, threshold=threshold)
|
|
407
407
|
# Electronegativity test
|
|
408
|
-
if cn_e:
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
for ratio in cn_r:
|
|
412
|
-
compositions.append((symbols, ox_states, ratio))
|
|
413
|
-
|
|
408
|
+
if cn_e and pauling_test(ox_states, electronegs):
|
|
409
|
+
for ratio in cn_r:
|
|
410
|
+
compositions.append((symbols, ox_states, ratio))
|
|
414
411
|
# Return list depending on whether we are interested in unique species combinations
|
|
415
412
|
# or just unique element combinations.
|
|
416
413
|
if species_unique:
|
|
@@ -429,9 +426,12 @@ def smact_validity(
|
|
|
429
426
|
composition: pymatgen.core.Composition | str,
|
|
430
427
|
use_pauling_test: bool = True,
|
|
431
428
|
include_alloys: bool = True,
|
|
432
|
-
oxidation_states_set: str = "icsd24",
|
|
433
429
|
check_metallicity: bool = False,
|
|
434
430
|
metallicity_threshold: float = 0.7,
|
|
431
|
+
oxidation_states_set: str | None = None,
|
|
432
|
+
include_zero: bool = False,
|
|
433
|
+
consensus: int = 3,
|
|
434
|
+
commonality: str = "medium",
|
|
435
435
|
) -> bool:
|
|
436
436
|
"""
|
|
437
437
|
Check if a composition is valid according to SMACT rules:
|
|
@@ -444,14 +444,24 @@ def smact_validity(
|
|
|
444
444
|
composition (Composition or str): Composition to check.
|
|
445
445
|
use_pauling_test (bool): Whether to apply the Pauling EN test.
|
|
446
446
|
include_alloys (bool): Consider pure metals valid automatically.
|
|
447
|
-
oxidation_states_set (str): Which set of oxidation states to use.
|
|
448
447
|
check_metallicity (bool): If True, consider high metallicity valid.
|
|
449
448
|
metallicity_threshold (float): Score threshold for metallicity validity.
|
|
449
|
+
oxidation_states_set (str): Which set of oxidation states to use. If specified it overrides the making of the oxidation states set.
|
|
450
|
+
include_zero (bool): Include oxidation state of zero in the filtered list. Default is False.
|
|
451
|
+
consensus (int): Minimum number of occurrences in literature for an ion to be considered valid. Default is 3.
|
|
452
|
+
commonality (str): Excludes species below a certain proportion of appearances in literature with respect to the total number of reports of a given element (after the consensus threshold has been applied). "low" includes all species, "medium" excludes rare species below 10% occurrence, and "high" excludes non-majority species below 50% occurrence. "main" selects the species with the highest occurrence for a given element. Users may also specify their own threshold (float or int). Default is "medium".
|
|
450
453
|
|
|
451
454
|
Returns:
|
|
452
455
|
bool: True if the composition is valid, False otherwise.
|
|
453
456
|
"""
|
|
454
457
|
from smact import _gcd_recursive, metals, neutral_ratios
|
|
458
|
+
from smact.utils.oxidation import ICSD24OxStatesFilter
|
|
459
|
+
|
|
460
|
+
if oxidation_states_set is not None and any([include_zero, consensus != 3, commonality != "medium"]):
|
|
461
|
+
warnings.warn(
|
|
462
|
+
"Parameters include_zero, consensus, and commonality are only used when oxidation_states_set is None",
|
|
463
|
+
stacklevel=2,
|
|
464
|
+
)
|
|
455
465
|
|
|
456
466
|
if isinstance(composition, str):
|
|
457
467
|
composition = Composition(composition)
|
|
@@ -484,7 +494,20 @@ def smact_validity(
|
|
|
484
494
|
electronegs = [e.pauling_eneg for e in smact_elems]
|
|
485
495
|
|
|
486
496
|
# Get oxidation states data
|
|
487
|
-
if oxidation_states_set
|
|
497
|
+
if oxidation_states_set is None:
|
|
498
|
+
ox_filter = ICSD24OxStatesFilter()
|
|
499
|
+
filtered_df = ox_filter.filter(consensus=consensus, include_zero=include_zero, commonality=commonality)
|
|
500
|
+
oxidation_dict = {
|
|
501
|
+
row["element"]: [int(x) for x in row["oxidation_state"].split()] for _, row in filtered_df.iterrows()
|
|
502
|
+
}
|
|
503
|
+
ox_combos = []
|
|
504
|
+
for el in smact_elems:
|
|
505
|
+
ox_el = oxidation_dict.get(el.symbol, None)
|
|
506
|
+
if ox_el is not None:
|
|
507
|
+
ox_combos.append(ox_el)
|
|
508
|
+
else:
|
|
509
|
+
return False
|
|
510
|
+
elif oxidation_states_set == "smact14":
|
|
488
511
|
ox_combos = [el.oxidation_states_smact14 for el in smact_elems]
|
|
489
512
|
elif oxidation_states_set == "icsd16":
|
|
490
513
|
ox_combos = [el.oxidation_states_icsd16 for el in smact_elems]
|
|
@@ -133,6 +133,33 @@ class TestCrystalSpace(unittest.TestCase):
|
|
|
133
133
|
# Clean up
|
|
134
134
|
shutil.rmtree("data")
|
|
135
135
|
|
|
136
|
+
def test_generate_composition_with_smact_custom(self):
|
|
137
|
+
save_dir = "data/binary/df_binary_label.pkl"
|
|
138
|
+
oxidation_states_sets = ["smact14", "icsd24"]
|
|
139
|
+
oxidation_states_sets_dict = {
|
|
140
|
+
"smact14": {"smact_allowed": 388},
|
|
141
|
+
"icsd24": {"smact_allowed": 342},
|
|
142
|
+
}
|
|
143
|
+
for ox_states in oxidation_states_sets:
|
|
144
|
+
with self.subTest(ox_states=ox_states):
|
|
145
|
+
smact_df = generate_composition_with_smact.generate_composition_with_smact_custom(
|
|
146
|
+
num_elements=2,
|
|
147
|
+
max_stoich=3,
|
|
148
|
+
max_atomic_num=20,
|
|
149
|
+
save_path=save_dir,
|
|
150
|
+
oxidation_states_set=ox_states,
|
|
151
|
+
)
|
|
152
|
+
self.assertIsInstance(smact_df, pd.DataFrame)
|
|
153
|
+
self.assertTrue(len(smact_df) == 1330)
|
|
154
|
+
self.assertTrue(
|
|
155
|
+
smact_df["smact_allowed"].sum() == oxidation_states_sets_dict[ox_states]["smact_allowed"]
|
|
156
|
+
)
|
|
157
|
+
# Check if the data was saved to disk
|
|
158
|
+
self.assertTrue(os.path.exists(save_dir))
|
|
159
|
+
|
|
160
|
+
# Clean up
|
|
161
|
+
shutil.rmtree("data")
|
|
162
|
+
|
|
136
163
|
@pytest.mark.skipif(
|
|
137
164
|
(
|
|
138
165
|
sys.platform == "win32"
|
|
@@ -13,6 +13,7 @@ from pymatgen.core import Composition
|
|
|
13
13
|
from tqdm import tqdm
|
|
14
14
|
|
|
15
15
|
from smact import Element, ordered_elements
|
|
16
|
+
from smact.data_loader import lookup_element_oxidation_states_custom
|
|
16
17
|
from smact.screening import smact_filter
|
|
17
18
|
|
|
18
19
|
warnings.simplefilter(action="ignore", category=UserWarning)
|
|
@@ -56,7 +57,7 @@ def generate_composition_with_smact(
|
|
|
56
57
|
max_atomic_num (int): the maximum atomic number. Defaults to 103.
|
|
57
58
|
num_processes (int): the number of processes to use. Defaults to None.
|
|
58
59
|
save_path (str): the path to save the results. Defaults to None.
|
|
59
|
-
oxidation_states_set (str): the oxidation states set to use. Options are "smact14", "icsd16", "icsd24", "pymatgen_sp"
|
|
60
|
+
oxidation_states_set (str): the oxidation states set to use. Options are "smact14", "icsd16", "icsd24", "pymatgen_sp". For reproducing the Faraday Discussions results, use "smact14". For custom oxidation states lists check generate_composition_with_smact_custom below.
|
|
60
61
|
|
|
61
62
|
Returns:
|
|
62
63
|
df (pd.DataFrame): A DataFrame of SMACT-generated compositions with boolean smact_allowed column.
|
|
@@ -142,3 +143,115 @@ def generate_composition_with_smact(
|
|
|
142
143
|
print(f"Saved to {save_path}")
|
|
143
144
|
|
|
144
145
|
return df
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def generate_composition_with_smact_custom(
|
|
149
|
+
num_elements: int = 2,
|
|
150
|
+
max_stoich: int = 8,
|
|
151
|
+
max_atomic_num: int = 103,
|
|
152
|
+
num_processes: int | None = None,
|
|
153
|
+
save_path: str | None = None,
|
|
154
|
+
oxidation_states_set: str | None = None,
|
|
155
|
+
) -> pd.DataFrame:
|
|
156
|
+
"""
|
|
157
|
+
Generate all possible compositions of a given number of elements and
|
|
158
|
+
filter them with SMACT.
|
|
159
|
+
|
|
160
|
+
Args:
|
|
161
|
+
num_elements (int): the number of elements in a compound. Defaults to 2.
|
|
162
|
+
max_stoich (int): the maximum stoichiometric coefficient. Defaults to 8.
|
|
163
|
+
max_atomic_num (int): the maximum atomic number. Defaults to 103.
|
|
164
|
+
num_processes (int): the number of processes to use. Defaults to None.
|
|
165
|
+
save_path (str): the path to save the results. Defaults to None.
|
|
166
|
+
oxidation_states_set (str): the path to the oxidation states file. Defaults to None.
|
|
167
|
+
|
|
168
|
+
Returns:
|
|
169
|
+
df (pd.DataFrame): A DataFrame of SMACT-generated compositions with boolean smact_allowed column.
|
|
170
|
+
|
|
171
|
+
"""
|
|
172
|
+
# 1. generate all possible combinations of elements
|
|
173
|
+
print("#1. Generating all possible combinations of elements...")
|
|
174
|
+
|
|
175
|
+
elements = [Element(element) for element in ordered_elements(1, max_atomic_num)]
|
|
176
|
+
combinations = list(itertools.combinations(elements, num_elements))
|
|
177
|
+
print(f"Number of generated combinations: {len(combinations)}")
|
|
178
|
+
|
|
179
|
+
# 2. generate all possible stoichiometric combinations
|
|
180
|
+
print("#2. Generating all possible stoichiometric combinations...")
|
|
181
|
+
|
|
182
|
+
pool = multiprocessing.Pool(processes=(multiprocessing.cpu_count() if num_processes is None else num_processes))
|
|
183
|
+
compounds = list(
|
|
184
|
+
tqdm(
|
|
185
|
+
pool.imap_unordered(
|
|
186
|
+
partial(
|
|
187
|
+
convert_formula,
|
|
188
|
+
num_elements=num_elements,
|
|
189
|
+
max_stoich=max_stoich,
|
|
190
|
+
),
|
|
191
|
+
combinations,
|
|
192
|
+
),
|
|
193
|
+
total=len(combinations),
|
|
194
|
+
)
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
pool.close()
|
|
198
|
+
pool.join()
|
|
199
|
+
# Flatten the list of lists into a single list
|
|
200
|
+
compounds = [item for sublist in compounds for item in sublist]
|
|
201
|
+
|
|
202
|
+
print(f"Number of generated compounds: {len(compounds)}")
|
|
203
|
+
compounds = list(set(compounds))
|
|
204
|
+
print(f"Number of generated compounds (unique): {len(compounds)}")
|
|
205
|
+
|
|
206
|
+
# 3. filter compounds with smact
|
|
207
|
+
print("#3. Filtering compounds with SMACT...")
|
|
208
|
+
|
|
209
|
+
ox_states_custom = lookup_element_oxidation_states_custom("all", oxidation_states_set, copy=False)
|
|
210
|
+
elements_pauling = [
|
|
211
|
+
Element(element)
|
|
212
|
+
for element in ordered_elements(1, max_atomic_num)
|
|
213
|
+
if element in ox_states_custom
|
|
214
|
+
and Element(element).pauling_eneg is not None
|
|
215
|
+
and Element(element).pauling_eneg >= Element("Fr").pauling_eneg
|
|
216
|
+
]
|
|
217
|
+
compounds_pauling = list(itertools.combinations(elements_pauling, num_elements))
|
|
218
|
+
|
|
219
|
+
pool = multiprocessing.Pool(processes=(multiprocessing.cpu_count() if num_processes is None else num_processes))
|
|
220
|
+
results = list(
|
|
221
|
+
tqdm(
|
|
222
|
+
pool.imap_unordered(
|
|
223
|
+
partial(
|
|
224
|
+
smact_filter,
|
|
225
|
+
threshold=max_stoich,
|
|
226
|
+
oxidation_states_set=oxidation_states_set,
|
|
227
|
+
),
|
|
228
|
+
compounds_pauling,
|
|
229
|
+
),
|
|
230
|
+
total=len(compounds_pauling),
|
|
231
|
+
)
|
|
232
|
+
)
|
|
233
|
+
pool.close()
|
|
234
|
+
pool.join()
|
|
235
|
+
|
|
236
|
+
# 4. make data frame of results
|
|
237
|
+
print("#4. Making data frame of results...")
|
|
238
|
+
# make dataframework with index is compound and columns are boolean smact results
|
|
239
|
+
smact_allowed = []
|
|
240
|
+
|
|
241
|
+
for result in results:
|
|
242
|
+
for res in result:
|
|
243
|
+
symbols_stoich = zip(res[0], res[2], strict=False)
|
|
244
|
+
composition_dict = dict(symbols_stoich)
|
|
245
|
+
smact_allowed.append(Composition(composition_dict).reduced_formula)
|
|
246
|
+
smact_allowed = list(set(smact_allowed))
|
|
247
|
+
print(f"Number of compounds allowed by SMACT: {len(smact_allowed)}")
|
|
248
|
+
|
|
249
|
+
df = pd.DataFrame(data=False, index=compounds, columns=["smact_allowed"])
|
|
250
|
+
df.loc[smact_allowed, "smact_allowed"] = True
|
|
251
|
+
|
|
252
|
+
if save_path is not None:
|
|
253
|
+
Path(save_path).parent.mkdir(parents=True, exist_ok=True)
|
|
254
|
+
df.to_pickle(save_path)
|
|
255
|
+
print(f"Saved to {save_path}")
|
|
256
|
+
|
|
257
|
+
return df
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -136,6 +136,7 @@ class ICSD24OxStatesFilter:
|
|
|
136
136
|
species_occurrences_df = self.ox_states_df[self.ox_states_df["oxidation_state"] != 0].reset_index(drop=True)
|
|
137
137
|
else:
|
|
138
138
|
species_occurrences_df = self.ox_states_df
|
|
139
|
+
|
|
139
140
|
species_occurrences_df = species_occurrences_df[
|
|
140
141
|
(species_occurrences_df.results_count >= consensus)
|
|
141
142
|
].reset_index(drop=True)
|
|
@@ -185,7 +186,6 @@ class ICSD24OxStatesFilter:
|
|
|
185
186
|
final_summary.append(f"{element} {oxidation_states}".strip())
|
|
186
187
|
else:
|
|
187
188
|
final_summary.append(element)
|
|
188
|
-
|
|
189
189
|
# Write the filtered oxidation states list to a txt file
|
|
190
190
|
if not filename.endswith(".txt"):
|
|
191
191
|
filename += ".txt"
|