File size: 4,120 Bytes
71741d2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import os
import numpy as np
import itertools
import shutil
from collections import Counter
from pymatgen.core import Structure
from smact import element_dictionary, metals, neutral_ratios
from smact.screening import pauling_test
from pymatgen.analysis.structure_matcher import StructureMatcher
from tqdm import tqdm

# --- FIX: Use relative paths for Hugging Face ---
input_folder = "generation_cifs"
output_folder = "valid_generation_cifs"
unique_folder = "unique_structures"
novel_folder = "novel_structures"
# ------------------------------------------------

def get_safe_space_group_number(structure):
    try:
        spg_info = structure.get_space_group_info()
        return spg_info[1] if spg_info else None
    except Exception:
        return None
    
def structure_validity(structure, cutoff=0.5):
    dist_mat = structure.distance_matrix
    dist_mat = dist_mat + np.diag(np.ones(dist_mat.shape[0]) * (cutoff + 10.))
    if dist_mat.min() < cutoff or structure.volume < 0.1:
        return False
    spg_number = get_safe_space_group_number(structure)
    if spg_number is None:
        return False
    return True

def smact_validity(atom_types, use_pauling_test=True, include_alloys=True):
    elem_counter = Counter(atom_types)
    elems = [(elem, elem_counter[elem]) for elem in sorted(elem_counter.keys())]
    comp, elem_counts = list(zip(*elems))
    elem_counts = np.array(elem_counts)
    elem_counts = elem_counts / np.gcd.reduce(elem_counts)
    count = tuple(elem_counts.astype("int").tolist())

    elem_symbols = tuple(comp)
    try:
        space = element_dictionary(elem_symbols)
    except:
        return False # Handle elements not in SMACT

    smact_elems = [e[1] for e in space.items()]
    electronegs = [e.pauling_eneg for e in smact_elems]
    ox_combos = [e.oxidation_states for e in smact_elems]

    if len(set(elem_symbols)) == 1:
        return True
    if include_alloys:
        is_metal_list = [elem_s in metals for elem_s in elem_symbols]
        if all(is_metal_list):
            return True

    threshold = np.max(count)
    oxn = 1
    for oxc in ox_combos:
        oxn *= len(oxc)
    if oxn > 1e7:
        return False

    for ox_states in itertools.product(*ox_combos):
        stoichs = [(c,) for c in count]
        cn_e, cn_r = neutral_ratios(ox_states, stoichs=stoichs, threshold=threshold)
        if cn_e:
            if use_pauling_test:
                electroneg_OK = pauling_test(ox_states, electronegs)
            else:
                electroneg_OK = True
            if electroneg_OK:
                return True
    return False

def get_atom_types_from_structure(structure):
    return [str(element) for element in structure.species]

# Create directories
os.makedirs(output_folder, exist_ok=True)
os.makedirs(unique_folder, exist_ok=True)
os.makedirs(novel_folder, exist_ok=True)

total = 0
structure_valid_count = 0
composition_valid_count = 0
both_valid_count = 0

if os.path.exists(input_folder):
    for filename in os.listdir(input_folder):
        if filename.endswith(".cif"):
            total += 1
            full_path = os.path.join(input_folder, filename)
            try:
                structure = Structure.from_file(full_path)
                atom_types = get_atom_types_from_structure(structure)

                struct_valid = structure_validity(structure)
                comp_valid = smact_validity(atom_types)

                if struct_valid:
                    structure_valid_count += 1
                if comp_valid:
                    composition_valid_count += 1
                if struct_valid and comp_valid:
                    both_valid_count += 1
                    shutil.copy(full_path, os.path.join(output_folder, filename))
            except Exception as e:
                print(f"Error processing {filename}: {e}")

print(f"\n=== Validity Summary ===")
print(f"Total CIFs checked:              {total}")
print(f"Structural validity passed:     {structure_valid_count}")
print(f"Compositional validity passed:  {composition_valid_count}")
print(f"Both valid (saved):             {both_valid_count}")