comparison larch_feff.py @ 0:edf7f8ccf4af draft

planemo upload for repository https://github.com/MaterialsGalaxy/larch-tools/tree/main/larch_feff commit 5be486890442dedfb327289d597e1c8110240735
author muon-spectroscopy-computational-project
date Tue, 14 Nov 2023 15:35:09 +0000
parents
children 8ee2cc3374fe
comparison
equal deleted inserted replaced
-1:000000000000 0:edf7f8ccf4af
1 import json
2 import re
3 import shutil
4 import sys
5 from pathlib import Path
6
7 from larch.xafs.feffrunner import feff6l
8
9 from pymatgen.io.cif import CifParser
10 from pymatgen.io.feff import Atoms, Header, Potential
11
12
13 def get_path_labels(paths_file):
14 is_meta = True
15 count = 0
16 a_path = {}
17 all_paths = {}
18 with open(paths_file) as datfile:
19 dat_lines = datfile.readlines()
20 for a_line in dat_lines:
21 count += 1
22 if re.match("-{15}", a_line.strip()) is not None:
23 is_meta = False
24 elif not is_meta:
25 if re.match(r"\s*\d*\s{4}\d*\s{3}", a_line) is not None:
26 if a_path:
27 all_paths[a_path["index"]] = a_path
28 line_data = a_line.split()
29 a_path = {
30 "index": line_data[0],
31 "nleg": line_data[1],
32 "degeneracy": line_data[2],
33 }
34 elif (
35 re.match(r"\s{6}x\s{11}y\s{5}", a_line) is None
36 ): # ignore the intermediate headings
37 line_data = a_line.split()
38 if "label" not in a_path:
39 a_path["label"] = line_data[4].replace("'", "")
40 else:
41 a_path["label"] += "." + line_data[4].replace("'", "")
42 if a_path and "index" in a_path:
43 all_paths[a_path["index"]] = a_path
44 return all_paths
45
46
47 def main(structure_file: str, file_format: dict):
48 crystal_f = Path(structure_file)
49 feff_dir = "feff"
50 feff_inp = "feff.inp"
51 header_inp = "header"
52 atoms_inp = "atoms"
53 potential_inp = "potential"
54 path = Path(feff_dir, feff_inp)
55 path.parent.mkdir(parents=True, exist_ok=True)
56
57 if file_format["format"] == "cif":
58 print(f"Parsing {crystal_f.name} and saving to {path}")
59 cif_parser = CifParser(crystal_f)
60 structures = cif_parser.get_structures()
61 length = len(structures)
62 if length != 1:
63 raise ValueError(
64 f"Execpted single structure in cif file but found {length}"
65 )
66 try:
67 aborsbing_atom = int(file_format["absorbing_atom"])
68 except ValueError:
69 # aborsbing_atom can be int or chemical symbol
70 aborsbing_atom = file_format["absorbing_atom"]
71
72 feff_header = Header(structures[0])
73 potential = Potential(structures[0], aborsbing_atom)
74 atoms = Atoms(structures[0], aborsbing_atom, file_format["radius"])
75 # if len(atoms.struct.sites) < len(potential.):
76
77 # print(atoms.as_dict())
78 feff_header.write_file(header_inp)
79 potential.write_file(potential_inp)
80 atoms.write_file(atoms_inp)
81 with open(path, "w") as feff_inp_file:
82 with open(header_inp) as f:
83 header_text = f.read()
84 print(header_text)
85 feff_inp_file.write(header_text + "\n")
86 with open(potential_inp) as f:
87 potential_text = f.readlines()
88 print(*potential_text)
89 feff_inp_file.writelines(potential_text + ["\n"])
90 with open(atoms_inp) as f:
91 atoms_text = f.readlines()
92 print(*atoms_text)
93 feff_inp_file.writelines(atoms_text + ["\n"])
94 if len(atoms_text) <= len(potential_text):
95 print(
96 "WARNING: Every potential in the structure must be represented"
97 " by at least one atom, consider increasing the radius"
98 )
99 else:
100 print(f"Copying {crystal_f.name} to {path}")
101 shutil.copy(crystal_f, path)
102
103 feff6l(folder=feff_dir, feffinp=feff_inp)
104
105 feff_files = "files.dat"
106 input_file = Path(feff_dir, feff_files)
107 # the .dat data is stored in fixed width strings
108 comma_positions = [13, 21, 32, 41, 48, 61]
109 paths_data = []
110 # get the list of paths info to assign labels to paths
111 try:
112 paths_info = get_path_labels(Path(feff_dir, "paths.dat"))
113 except FileNotFoundError as err:
114 raise FileNotFoundError(
115 "paths.dat does not exist, which implies FEFF failed to run"
116 ) from err
117
118 print("Reading from: " + str(input_file))
119 with open(input_file) as datfile:
120 # Read until we find the line at the end of the header
121 line = datfile.readline()
122 while not re.match("-+", line.strip()):
123 line = datfile.readline()
124
125 # Build headers
126 line = datfile.readline()
127 header = ""
128 start = 0
129 for end in comma_positions:
130 header += line[start:end] + ","
131 start = end
132 header += f" {'label':30s}, {'select':6s}\n"
133 paths_data.append(header)
134
135 # Read data
136 line = datfile.readline()
137 while line:
138 data = ""
139 start = 0
140 for end in comma_positions[:-1]:
141 data += line[start:end] + ","
142 start = end
143
144 # Last column needs padding to align
145 data += line[start:-1] + " ,"
146
147 # Add label and select column
148 path_id = int(data[5:9])
149 try:
150 label = paths_info[str(path_id)]["label"] + f".{path_id}"
151 except KeyError as err:
152 msg = f"{path_id} not in {paths_info.keys()}"
153 raise KeyError(msg) from err
154 data += f" {label:30s}, {0:6d}\n"
155 paths_data.append(data)
156 line = datfile.readline()
157
158 with open("out.csv", "w") as out:
159 out.writelines(paths_data)
160
161
162 if __name__ == "__main__":
163 structure_file = sys.argv[1]
164 input_values = json.load(open(sys.argv[2], "r", encoding="utf-8"))
165 main(structure_file, input_values["format"])