Mercurial > repos > muon-spectroscopy-computational-project > muspinsim_config
comparison build_file.py @ 0:c70012022f0f draft
planemo upload for repository https://github.com/muon-spectroscopy-computational-project/muon-galaxy-tools/main/muspinsim_config commit d130cf2c46d933fa9d0214ddbd5ddf860f322dc4
author | muon-spectroscopy-computational-project |
---|---|
date | Thu, 25 Aug 2022 16:16:47 +0000 |
parents | |
children | 331d0776abb4 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:c70012022f0f |
---|---|
1 import json | |
2 import re | |
3 import sys | |
4 | |
5 from muspinsim import MuSpinInput | |
6 | |
7 | |
8 def write_file(file_name, content): | |
9 """ | |
10 Write muspinsim file | |
11 :param file_name: name of file | |
12 :param content: list of strings containing blocks to write | |
13 """ | |
14 with open(file_name, "w") as f: | |
15 f.write( | |
16 """ | |
17 ####################################################### | |
18 #Muspinsim Input File | |
19 #Generated using Muon Galaxy Tool Muspinsim_Input | |
20 #######################################################\n\n""" | |
21 ) | |
22 f.write("".join(content)) | |
23 | |
24 | |
25 def build_block(title, vals): | |
26 """ | |
27 Build keyword block | |
28 :param title: string - Keyword | |
29 :param vals: list of strings - lines containing values for keyword | |
30 :return: A string containing formatted keyword block | |
31 """ | |
32 return "{0}\n {1}\n".format(title, "\n ".join(vals)) | |
33 | |
34 | |
35 def format_entry(entry): | |
36 """ | |
37 Helper function to remove whitespace between function parameters | |
38 and remove ',' or ';' inbetween parameters | |
39 :param entry: string - user entry | |
40 :return: string containing only valid parameters | |
41 """ | |
42 stck = [] | |
43 new_str = "" | |
44 for i, char in enumerate(entry): | |
45 if char == "(": | |
46 stck.append(i) | |
47 elif char == ")": | |
48 if len(stck) == 0: | |
49 raise ValueError( | |
50 "Could not parse entry {0}" | |
51 "brackets mismatch - unexpected ')' " | |
52 "found on char {1}".format(entry, i) | |
53 ) | |
54 stck.pop() | |
55 elif char == " " and len(stck) > 0: | |
56 continue | |
57 | |
58 # remove ',' between functions | |
59 elif char in [",", ";"] and len(stck) == 0: | |
60 new_str += " " | |
61 continue | |
62 new_str += char | |
63 | |
64 if len(stck) != 0: | |
65 raise ValueError( | |
66 "Could not parse entry {0}" | |
67 "brackets mismatch - unclosed '(' found on char(s): {1}".format( | |
68 entry, stck | |
69 ) | |
70 ) | |
71 return new_str | |
72 | |
73 | |
74 def split_into_args(entry, nargs=1): | |
75 """ | |
76 Helper function to split input into a list of args | |
77 :param entry: a string containing a user inputted line | |
78 :param nargs: number of expected arguments | |
79 :return: a list of arguments found | |
80 :exception: ValueError - if number of arguments | |
81 found does not match expected (nargs) | |
82 """ | |
83 | |
84 # remove square brackets and extra whitespace/newline | |
85 content = " ".join(entry.replace("[", "").replace("]", "").split()) | |
86 | |
87 # remove whitespace in between expressions/functions | |
88 # remove commas/semicolons in between expressions/functions | |
89 # split on whitespace to separate args | |
90 | |
91 content = re.split(r"\s", format_entry(content)) | |
92 chars = [elem.strip() for elem in content if elem != ""] | |
93 if len(chars) != nargs: | |
94 raise ValueError( | |
95 "Could not parse entry {0}" | |
96 " incorrect number of args" | |
97 " found {1}:\n({2})\nBut expected {3}".format( | |
98 entry, len(chars), chars, nargs | |
99 ) | |
100 ) | |
101 return chars | |
102 | |
103 | |
104 def parse_matrix(entry_string, size): | |
105 """ | |
106 Helper function to parse and format matrix/vector | |
107 to be readable by Muspinsim | |
108 :param entry_string: a user input string for a matrix/vector | |
109 :param size: (x, y) integer tuple: dimensions of matrix | |
110 :return: a list of strings of length y, each string | |
111 containing x elements (space separated) | |
112 """ | |
113 content = split_into_args(entry_string, nargs=size[0] * size[1]) | |
114 return [ | |
115 " ".join(content[x: x + size[0]]) | |
116 for x in range(0, len(content), size[0]) | |
117 ] | |
118 | |
119 | |
120 def parse_interactions(interaction): | |
121 """ | |
122 Helper function to build keyword blocks for all | |
123 interaction parameters entered | |
124 (hyperfine, zeeman, dipolar, quadrupolar and dissipation) | |
125 | |
126 :param interaction: a dictionary containing all interaction parameters | |
127 :return: a string containing several formatted blocks | |
128 """ | |
129 | |
130 options = interaction["interaction_options"] | |
131 interaction_type = options["interaction"] | |
132 try: | |
133 return { | |
134 "zeeman": lambda options: build_block( | |
135 "zeeman {0}".format(options["zeeman_index"]), | |
136 parse_matrix(options["zeeman_vector"], (3, 1)), | |
137 ), | |
138 "hyperfine": lambda options: build_block( | |
139 "hyperfine {0} {1}".format( | |
140 options["hfine_index"], | |
141 options["hfine_e_index"] | |
142 if options["hfine_e_index"] | |
143 else "", | |
144 ).strip(), | |
145 parse_matrix(options["hfine_matrix"], (3, 3)), | |
146 ), | |
147 "dipolar": lambda options: build_block( | |
148 "dipolar {0} {1}".format( | |
149 options["di_index"], options["di_index_2"] | |
150 ), | |
151 parse_matrix(options["di_vector"], (3, 1)), | |
152 ), | |
153 "quadrupolar": lambda options: build_block( | |
154 "quadrupolar {0}".format(options["quad_index"]), | |
155 parse_matrix(options["quad_matrix"], (3, 3)), | |
156 ), | |
157 "dissipation": lambda options: build_block( | |
158 "dissipation {0}".format(options["dis_index"]), | |
159 [options["dis_val"]], | |
160 ), | |
161 }.get(interaction_type)(options) | |
162 except ValueError as e: | |
163 raise ValueError("Error occurred when parsing {0}".format(e)) | |
164 | |
165 | |
166 def parse_orientation(orientation): | |
167 """ | |
168 Helper function to parse orientation keyword arguments | |
169 :param orientation: a dictionary containing one set of | |
170 orientation arguments | |
171 :return: a formatted string | |
172 """ | |
173 | |
174 options = orientation["orientation_options"] | |
175 preset = options["orientation_preset"] | |
176 | |
177 return { | |
178 "zcw": lambda options: "zcw({0})".format( | |
179 " ".join(split_into_args(options["zcw_n"], 1)) | |
180 ), | |
181 "eulrange": lambda options: "eulrange({0})".format( | |
182 " ".join(split_into_args(options["eul_n"], 1)) | |
183 ), | |
184 "2_polar": lambda options: "{0} {1}".format( | |
185 " ".join(split_into_args(options["theta"], 1)), | |
186 " ".join(split_into_args(options["phi"], 1)), | |
187 ), | |
188 "3_euler": lambda options: "{0} {1} {2}".format( | |
189 " ".join(split_into_args(options["eul_1"], 1)), | |
190 " ".join(split_into_args(options["eul_2"], 1)), | |
191 " ".join(split_into_args(options["eul_3"], 1)), | |
192 ), | |
193 "4_euler": lambda options: "{0} {1} {2} {3}".format( | |
194 " ".join(split_into_args(options["eul_1"], 1)), | |
195 " ".join(split_into_args(options["eul_2"], 1)), | |
196 " ".join(split_into_args(options["eul_3"], 1)), | |
197 options["weight"], | |
198 ), | |
199 }.get(preset)(options) | |
200 | |
201 | |
202 def parse_polarization(polarization): | |
203 """ | |
204 Helper function to parse polarization keyword arguments | |
205 :param polarization: a dictionary containing one set | |
206 of polarization arguments | |
207 :return: a formatted string | |
208 """ | |
209 options = polarization["polarization_options"] | |
210 preset = options["polarization_preset"] | |
211 if preset != "custom": | |
212 return preset | |
213 else: | |
214 try: | |
215 return " ".join(split_into_args(options["polarization"], 1)) | |
216 except ValueError: | |
217 return " ".join(split_into_args(options["polarization"], 3)) | |
218 | |
219 | |
220 def parse_field(field): | |
221 """ | |
222 Helper function to parse field keyword arguments | |
223 :param field: a dictionary containing one set of field arguments | |
224 :return: a formatted string | |
225 """ | |
226 try: | |
227 return " ".join(split_into_args(field["field"], 1)) | |
228 except ValueError: | |
229 return " ".join(split_into_args(field["field"], 3)) | |
230 | |
231 | |
232 def parse_fitting_variables(fitting_variables): | |
233 """ | |
234 Helper function to parse field keyword fitting_variables | |
235 :param fitting_variables: a dictionary containing one set of | |
236 arguments | |
237 :return: a formatted string | |
238 """ | |
239 return "{0} {1} {2} {3}".format( | |
240 fitting_variables["var_name"].strip().replace(" ", "_"), | |
241 " ".join(split_into_args(fitting_variables["start_val"], 1)) | |
242 if fitting_variables["start_val"].strip() != "" | |
243 else "", | |
244 " ".join(split_into_args(fitting_variables["min_bound"], 1)) | |
245 if fitting_variables["min_bound"].strip() != "" | |
246 else "", | |
247 " ".join(split_into_args(fitting_variables["max_bound"], 1)) | |
248 if fitting_variables["max_bound"].strip() != "" | |
249 else "", | |
250 ).strip() | |
251 | |
252 | |
253 def parse_spin(spin): | |
254 if spin["spin_preset"] != "custom": | |
255 return spin["spin_preset"] | |
256 else: | |
257 elem_name = spin["spin"].strip() | |
258 if elem_name not in ['e', 'mu']: | |
259 elem_name = elem_name.capitalize() | |
260 return "{0}{1}".format( | |
261 int(spin["atomic_mass"]) if spin["atomic_mass"] else "", | |
262 elem_name | |
263 ).strip() | |
264 | |
265 | |
266 parse_func_dict = { | |
267 "spins": lambda values: build_block( | |
268 "spins", | |
269 [ | |
270 " ".join( | |
271 [ | |
272 parse_spin(entry["spin_options"]) | |
273 for entry in values | |
274 ] | |
275 ) | |
276 ], | |
277 ), | |
278 # either 1x3 vector or scalar or function | |
279 "fields": lambda values: build_block( | |
280 "field", [parse_field(entry) for entry in values] | |
281 ), | |
282 # either scalar or single function | |
283 "times": lambda values: build_block( | |
284 "time", | |
285 [ | |
286 " ".join(split_into_args(entry["time"], 1)) | |
287 for entry in values | |
288 ], | |
289 ), | |
290 # either scalar or single function | |
291 "temperatures": lambda values: build_block( | |
292 "temperature", | |
293 [ | |
294 " ".join(split_into_args(entry["temperature"], 1)) | |
295 for entry in values | |
296 ], | |
297 ), | |
298 "x_axis": lambda value: build_block("x_axis", [value]), | |
299 "y_axis": lambda value: build_block("y_axis", [value]), | |
300 "average_axes": lambda values: build_block( | |
301 "average_axes", values | |
302 ), | |
303 "experiment_preset": lambda value: build_block( | |
304 "experiment", [value] | |
305 ), | |
306 "orientations": lambda values: build_block( | |
307 "orientation {0}".format(euler_convention), | |
308 [parse_orientation(entry) for entry in values], | |
309 ), | |
310 "interactions": lambda values: "".join( | |
311 [parse_interactions(entry) for entry in values] | |
312 ), | |
313 "polarizations": lambda values: build_block( | |
314 "polarization", | |
315 [parse_polarization(entry) for entry in values], | |
316 ), | |
317 "fitting": lambda value: build_block( | |
318 "fitting_data", ['load("fitting_data.dat")'] | |
319 ), | |
320 "fitting_method": lambda value: build_block( | |
321 "fitting_method", [value] | |
322 ), | |
323 "fitting_variables": lambda values: build_block( | |
324 "fitting_variables", | |
325 [parse_fitting_variables(entry) for entry in values], | |
326 ), | |
327 "fitting_tolerance": lambda value: build_block( | |
328 "fitting_tolerance", | |
329 [str(value)], | |
330 ), | |
331 } | |
332 euler_convention = 'ZYZ' | |
333 | |
334 | |
335 def main(): | |
336 input_json_path = sys.argv[1] | |
337 mu_params = json.load(open(input_json_path, "r")) | |
338 | |
339 out_file_name = mu_params["out_file_prefix"].strip().replace(" ", "_") | |
340 | |
341 # combine all sections | |
342 mu_params = { | |
343 **mu_params["spins"], | |
344 **mu_params["interaction_params"], | |
345 **mu_params["experiment_params"], | |
346 **mu_params["fitting_params"]["fitting_options"], | |
347 } | |
348 | |
349 # get experiment parameters | |
350 experiment = mu_params["experiment"] | |
351 mu_params = {**mu_params, **experiment} | |
352 | |
353 if experiment["experiment_preset"] == "custom": | |
354 del mu_params["experiment_preset"] | |
355 del mu_params["experiment"] | |
356 | |
357 global euler_convention | |
358 euler_convention = mu_params["euler_convention"] | |
359 | |
360 err_found = False | |
361 file_contents = [ | |
362 build_block("name", [out_file_name.strip().replace(" ", "_")]) | |
363 ] | |
364 for keyword, val in mu_params.items(): | |
365 if val and val not in ["None"]: | |
366 try: | |
367 keyword_func = parse_func_dict.get(keyword) | |
368 if keyword_func: | |
369 file_contents.append(keyword_func(val)) | |
370 | |
371 except ValueError as e: | |
372 sys.stderr.write( | |
373 "Error occurred when parsing {0}\n{1}".format( | |
374 keyword, str(e) | |
375 ) | |
376 ) | |
377 err_found = True | |
378 | |
379 if err_found: | |
380 sys.exit(1) | |
381 | |
382 write_file("outfile.in", file_contents) | |
383 | |
384 try: | |
385 MuSpinInput(open("outfile.in")) | |
386 except Exception as e: | |
387 sys.stdout.write( | |
388 "Warning, This created file may not work properly. " | |
389 "Error(s) encountered when trying to parse the file : {0}".format( | |
390 str(e) | |
391 ) | |
392 ) | |
393 sys.exit(1) | |
394 | |
395 | |
396 if __name__ == "__main__": | |
397 main() |