Mercurial > repos > muon-spectroscopy-computational-project > muspinsim_config
comparison build_file.py @ 3:331d0776abb4 draft
planemo upload for repository https://github.com/muon-spectroscopy-computational-project/muon-galaxy-tools/main/muspinsim_config commit 4f06b404d8b7fb83995f3052faa7e2ec7811f507
author | muon-spectroscopy-computational-project |
---|---|
date | Fri, 03 Feb 2023 15:39:07 +0000 |
parents | c70012022f0f |
children | e1e338f56656 |
comparison
equal
deleted
inserted
replaced
2:52d3a28902e8 | 3:331d0776abb4 |
---|---|
9 """ | 9 """ |
10 Write muspinsim file | 10 Write muspinsim file |
11 :param file_name: name of file | 11 :param file_name: name of file |
12 :param content: list of strings containing blocks to write | 12 :param content: list of strings containing blocks to write |
13 """ | 13 """ |
14 with open(file_name, "w") as f: | 14 with open(file_name, "w", encoding="utf-8") as file: |
15 f.write( | 15 file.write( |
16 """ | 16 """ |
17 ####################################################### | 17 ####################################################### |
18 #Muspinsim Input File | 18 # Muspinsim Input File |
19 #Generated using Muon Galaxy Tool Muspinsim_Input | 19 # Generated using Muon Galaxy Tool Muspinsim_Input |
20 #######################################################\n\n""" | 20 #######################################################\n\n""" |
21 ) | 21 ) |
22 f.write("".join(content)) | 22 file.write("".join(content)) |
23 | 23 |
24 | 24 |
25 def build_block(title, vals): | 25 def build_block(title, vals): |
26 """ | 26 """ |
27 Build keyword block | 27 Build keyword block |
45 if char == "(": | 45 if char == "(": |
46 stck.append(i) | 46 stck.append(i) |
47 elif char == ")": | 47 elif char == ")": |
48 if len(stck) == 0: | 48 if len(stck) == 0: |
49 raise ValueError( | 49 raise ValueError( |
50 "Could not parse entry {0}" | 50 f"Could not parse entry {entry} brackets mismatch - " |
51 "brackets mismatch - unexpected ')' " | 51 f"unexpected ')' found on char {i}" |
52 "found on char {1}".format(entry, i) | |
53 ) | 52 ) |
54 stck.pop() | 53 stck.pop() |
55 elif char == " " and len(stck) > 0: | 54 elif char == " " and len(stck) > 0: |
56 continue | 55 continue |
57 | 56 |
61 continue | 60 continue |
62 new_str += char | 61 new_str += char |
63 | 62 |
64 if len(stck) != 0: | 63 if len(stck) != 0: |
65 raise ValueError( | 64 raise ValueError( |
66 "Could not parse entry {0}" | 65 f"Could not parse entry {entry} brackets mismatch - unclosed '(' " |
67 "brackets mismatch - unclosed '(' found on char(s): {1}".format( | 66 f"found on char(s): {stck}" |
68 entry, stck | |
69 ) | |
70 ) | 67 ) |
71 return new_str | 68 return new_str |
72 | 69 |
73 | 70 |
74 def split_into_args(entry, nargs=1): | 71 def split_into_args(entry, nargs=1): |
90 | 87 |
91 content = re.split(r"\s", format_entry(content)) | 88 content = re.split(r"\s", format_entry(content)) |
92 chars = [elem.strip() for elem in content if elem != ""] | 89 chars = [elem.strip() for elem in content if elem != ""] |
93 if len(chars) != nargs: | 90 if len(chars) != nargs: |
94 raise ValueError( | 91 raise ValueError( |
95 "Could not parse entry {0}" | 92 f"Could not parse entry {entry} incorrect number of args found " |
96 " incorrect number of args" | 93 f"{len(chars)}:\n({chars})\nBut expected {nargs}" |
97 " found {1}:\n({2})\nBut expected {3}".format( | |
98 entry, len(chars), chars, nargs | |
99 ) | |
100 ) | 94 ) |
101 return chars | 95 return chars |
102 | 96 |
103 | 97 |
104 def parse_matrix(entry_string, size): | 98 def parse_matrix(entry_string, size): |
105 """ | 99 """ |
106 Helper function to parse and format matrix/vector | 100 Helper function to parse and format matrix/vector |
107 to be readable by Muspinsim | 101 to be readable by MuSpinSim |
108 :param entry_string: a user input string for a matrix/vector | 102 :param entry_string: a user input string for a matrix/vector |
109 :param size: (x, y) integer tuple: dimensions of matrix | 103 :param size: (x, y) integer tuple: dimensions of matrix |
110 :return: a list of strings of length y, each string | 104 :return: a list of strings of length y, each string |
111 containing x elements (space separated) | 105 containing x elements (space separated) |
112 """ | 106 """ |
134 "zeeman": lambda options: build_block( | 128 "zeeman": lambda options: build_block( |
135 "zeeman {0}".format(options["zeeman_index"]), | 129 "zeeman {0}".format(options["zeeman_index"]), |
136 parse_matrix(options["zeeman_vector"], (3, 1)), | 130 parse_matrix(options["zeeman_vector"], (3, 1)), |
137 ), | 131 ), |
138 "hyperfine": lambda options: build_block( | 132 "hyperfine": lambda options: build_block( |
139 "hyperfine {0} {1}".format( | 133 ( |
140 options["hfine_index"], | 134 f"hyperfine {options['hfine_index']} " |
141 options["hfine_e_index"] | 135 f"""{ |
142 if options["hfine_e_index"] | 136 options['hfine_e_index'] |
143 else "", | 137 if options['hfine_e_index'] else '' |
138 }""" | |
144 ).strip(), | 139 ).strip(), |
145 parse_matrix(options["hfine_matrix"], (3, 3)), | 140 parse_matrix(options["hfine_matrix"], (3, 3)), |
146 ), | 141 ), |
147 "dipolar": lambda options: build_block( | 142 "dipolar": lambda options: build_block( |
148 "dipolar {0} {1}".format( | 143 f"dipolar {options['di_index']} {options['di_index_2']}", |
149 options["di_index"], options["di_index_2"] | |
150 ), | |
151 parse_matrix(options["di_vector"], (3, 1)), | 144 parse_matrix(options["di_vector"], (3, 1)), |
152 ), | 145 ), |
153 "quadrupolar": lambda options: build_block( | 146 "quadrupolar": lambda options: build_block( |
154 "quadrupolar {0}".format(options["quad_index"]), | 147 f"quadrupolar {options['quad_index']}", |
155 parse_matrix(options["quad_matrix"], (3, 3)), | 148 parse_matrix(options["quad_matrix"], (3, 3)), |
156 ), | 149 ), |
157 "dissipation": lambda options: build_block( | 150 "dissipation": lambda options: build_block( |
158 "dissipation {0}".format(options["dis_index"]), | 151 f"dissipation {options['dis_index']}", |
159 [options["dis_val"]], | 152 [options["dis_val"]], |
160 ), | 153 ), |
161 }.get(interaction_type)(options) | 154 }.get(interaction_type)(options) |
162 except ValueError as e: | 155 except ValueError as exc: |
163 raise ValueError("Error occurred when parsing {0}".format(e)) | 156 raise ValueError(f"Error occurred when parsing {exc}") from exc |
164 | 157 |
165 | 158 |
166 def parse_orientation(orientation): | 159 def parse_orientation(orientation): |
167 """ | 160 """ |
168 Helper function to parse orientation keyword arguments | 161 Helper function to parse orientation keyword arguments |
215 return " ".join(split_into_args(options["polarization"], 1)) | 208 return " ".join(split_into_args(options["polarization"], 1)) |
216 except ValueError: | 209 except ValueError: |
217 return " ".join(split_into_args(options["polarization"], 3)) | 210 return " ".join(split_into_args(options["polarization"], 3)) |
218 | 211 |
219 | 212 |
220 def parse_field(field): | 213 def parse_field(field, field_type): |
221 """ | 214 """ |
222 Helper function to parse field keyword arguments | 215 Helper function to parse field keyword arguments |
223 :param field: a dictionary containing one set of field arguments | 216 :param field: a dictionary containing one set of field arguments |
217 :param field_type: a string giving the type of field, either field or | |
218 intrinsic_field | |
224 :return: a formatted string | 219 :return: a formatted string |
225 """ | 220 """ |
226 try: | 221 try: |
227 return " ".join(split_into_args(field["field"], 1)) | 222 return " ".join(split_into_args(field[field_type], 1)) |
228 except ValueError: | 223 except ValueError: |
229 return " ".join(split_into_args(field["field"], 3)) | 224 return " ".join(split_into_args(field[field_type], 3)) |
230 | 225 |
231 | 226 |
232 def parse_fitting_variables(fitting_variables): | 227 def parse_fitting_variables(fitting_variables): |
233 """ | 228 """ |
234 Helper function to parse field keyword fitting_variables | 229 Helper function to parse field keyword fitting_variables |
249 else "", | 244 else "", |
250 ).strip() | 245 ).strip() |
251 | 246 |
252 | 247 |
253 def parse_spin(spin): | 248 def parse_spin(spin): |
249 """ | |
250 Helper function for parsing a spin | |
251 :param spin: a dictionary containing a spin object from the config either | |
252 just a spin_preset or a custom value with a name and | |
253 atomic_mass | |
254 """ | |
254 if spin["spin_preset"] != "custom": | 255 if spin["spin_preset"] != "custom": |
255 return spin["spin_preset"] | 256 return spin["spin_preset"] |
256 else: | 257 else: |
257 elem_name = spin["spin"].strip() | 258 elem_name = spin["spin"].strip() |
258 if elem_name not in ['e', 'mu']: | 259 if elem_name not in ["e", "mu"]: |
259 elem_name = elem_name.capitalize() | 260 elem_name = elem_name.capitalize() |
260 return "{0}{1}".format( | 261 return ( |
261 int(spin["atomic_mass"]) if spin["atomic_mass"] else "", | 262 f"{int(spin['atomic_mass']) if spin['atomic_mass'] else ''}" |
262 elem_name | 263 f"{elem_name}" |
263 ).strip() | 264 ).strip() |
265 | |
266 | |
267 def parse_celio(celio_params): | |
268 """ | |
269 Helper function for parsing Celio's method parameters | |
270 :param celio_params: a dictionary containing the parameters for Celio's | |
271 method | |
272 """ | |
273 options = celio_params["celio_options"] | |
274 if not options["celio_enabled"]: | |
275 return "" | |
276 else: | |
277 # Now have celio_k and potentially celio_averages | |
278 celio_k = options["celio_k"] | |
279 celio_averages = options["celio_averages"] | |
280 | |
281 # As celio_averages is optional so may be None | |
282 if celio_averages is None: | |
283 celio_averages = "" | |
284 | |
285 return build_block("celio", [f"{celio_k} {celio_averages}".strip()]) | |
264 | 286 |
265 | 287 |
266 parse_func_dict = { | 288 parse_func_dict = { |
267 "spins": lambda values: build_block( | 289 "spins": lambda values: build_block( |
268 "spins", | 290 "spins", |
269 [ | 291 [" ".join([parse_spin(entry["spin_options"]) for entry in values])], |
270 " ".join( | |
271 [ | |
272 parse_spin(entry["spin_options"]) | |
273 for entry in values | |
274 ] | |
275 ) | |
276 ], | |
277 ), | 292 ), |
278 # either 1x3 vector or scalar or function | 293 # either 1x3 vector or scalar or function |
279 "fields": lambda values: build_block( | 294 "fields": lambda values: build_block( |
280 "field", [parse_field(entry) for entry in values] | 295 "field", [parse_field(entry, "field") for entry in values] |
296 ), | |
297 "intrinsic_fields": lambda values: build_block( | |
298 "intrinsic_field", | |
299 [parse_field(entry, "intrinsic_field") for entry in values], | |
281 ), | 300 ), |
282 # either scalar or single function | 301 # either scalar or single function |
283 "times": lambda values: build_block( | 302 "times": lambda values: build_block( |
284 "time", | 303 "time", |
285 [ | 304 [" ".join(split_into_args(entry["time"], 1)) for entry in values], |
286 " ".join(split_into_args(entry["time"], 1)) | |
287 for entry in values | |
288 ], | |
289 ), | 305 ), |
290 # either scalar or single function | 306 # either scalar or single function |
291 "temperatures": lambda values: build_block( | 307 "temperatures": lambda values: build_block( |
292 "temperature", | 308 "temperature", |
293 [ | 309 [ |
294 " ".join(split_into_args(entry["temperature"], 1)) | 310 " ".join(split_into_args(entry["temperature"], 1)) |
295 for entry in values | 311 for entry in values |
296 ], | 312 ], |
297 ), | 313 ), |
298 "x_axis": lambda value: build_block("x_axis", [value]), | 314 "axes_options": { |
299 "y_axis": lambda value: build_block("y_axis", [value]), | 315 "x_axis_options": { |
300 "average_axes": lambda values: build_block( | 316 "x_axis": lambda value: build_block("x_axis", [value]), |
301 "average_axes", values | 317 "average_axes": lambda values: build_block( |
302 ), | 318 "average_axes", values), |
303 "experiment_preset": lambda value: build_block( | 319 }, |
304 "experiment", [value] | 320 "x_axis": lambda value: build_block("x_axis", [value]), |
305 ), | 321 "y_axis": lambda value: build_block("y_axis", [value]), |
322 "average_axes": lambda values: build_block( | |
323 "average_axes", values) | |
324 }, | |
325 "average_axes": lambda values: build_block("average_axes", values), | |
326 "experiment_preset": lambda value: build_block("experiment", [value]), | |
306 "orientations": lambda values: build_block( | 327 "orientations": lambda values: build_block( |
307 "orientation {0}".format(euler_convention), | 328 f"orientation {EULER_CONVENTION}", |
308 [parse_orientation(entry) for entry in values], | 329 [parse_orientation(entry) for entry in values], |
309 ), | 330 ), |
310 "interactions": lambda values: "".join( | 331 "interactions": lambda values: "".join( |
311 [parse_interactions(entry) for entry in values] | 332 [parse_interactions(entry) for entry in values] |
312 ), | 333 ), |
313 "polarizations": lambda values: build_block( | 334 "polarizations": lambda values: build_block( |
314 "polarization", | 335 "polarization", |
315 [parse_polarization(entry) for entry in values], | 336 [parse_polarization(entry) for entry in values], |
316 ), | 337 ), |
338 "celio_params": parse_celio, | |
317 "fitting": lambda value: build_block( | 339 "fitting": lambda value: build_block( |
318 "fitting_data", ['load("fitting_data.dat")'] | 340 "fitting_data", ['load("fitting_data.dat")'] |
319 ), | 341 ), |
320 "fitting_method": lambda value: build_block( | 342 "fitting_method": lambda value: build_block("fitting_method", [value]), |
321 "fitting_method", [value] | |
322 ), | |
323 "fitting_variables": lambda values: build_block( | 343 "fitting_variables": lambda values: build_block( |
324 "fitting_variables", | 344 "fitting_variables", |
325 [parse_fitting_variables(entry) for entry in values], | 345 [parse_fitting_variables(entry) for entry in values], |
326 ), | 346 ), |
327 "fitting_tolerance": lambda value: build_block( | 347 "fitting_tolerance": lambda value: build_block( |
328 "fitting_tolerance", | 348 "fitting_tolerance", |
329 [str(value)], | 349 [str(value)], |
330 ), | 350 ), |
331 } | 351 } |
332 euler_convention = 'ZYZ' | 352 EULER_CONVENTION = "ZYZ" |
353 | |
354 # Gives replacement values in the case a parameter is unassigned | |
355 parse_none_dict = { | |
356 # Allow average_axis to be None as by default is orientation in | |
357 # muspinsim but letting the UI present this here instead | |
358 "average_axes": ["none"], | |
359 } | |
360 | |
361 | |
362 def parse_dict(dictionary, params, file_contents) -> bool: | |
363 """ | |
364 Helper function for parsing nested dictionaries defined above | |
365 containing parse functions | |
366 :returns: Whether an error occurred | |
367 """ | |
368 | |
369 err_found = False | |
370 for keyword, val in params.items(): | |
371 | |
372 # Either don't allow the value to be None or replace | |
373 # with value in the parse_none_dict above | |
374 should_assign = val and val not in ["None"] | |
375 if not should_assign and keyword in parse_none_dict: | |
376 should_assign = keyword in parse_none_dict | |
377 val = parse_none_dict[keyword] | |
378 | |
379 if should_assign: | |
380 try: | |
381 keyword_func = dictionary.get(keyword) | |
382 # Check for nested dict, and add that contents as well if found | |
383 if isinstance(keyword_func, dict): | |
384 err_found = err_found or parse_dict( | |
385 keyword_func, val, file_contents) | |
386 else: | |
387 if keyword_func: | |
388 file_contents.append(keyword_func(val)) | |
389 | |
390 except ValueError as exc: | |
391 sys.stderr.write( | |
392 f"Error occurred when parsing {keyword}\n{str(exc)}" | |
393 ) | |
394 err_found = True | |
395 return err_found | |
333 | 396 |
334 | 397 |
335 def main(): | 398 def main(): |
399 """ | |
400 Entry point | |
401 """ | |
336 input_json_path = sys.argv[1] | 402 input_json_path = sys.argv[1] |
337 mu_params = json.load(open(input_json_path, "r")) | 403 mu_params = json.load(open(input_json_path, "r", encoding="utf-8")) |
338 | 404 |
339 out_file_name = mu_params["out_file_prefix"].strip().replace(" ", "_") | 405 out_file_name = mu_params["out_file_prefix"].strip().replace(" ", "_") |
340 | 406 |
341 # combine all sections | 407 # combine all sections |
342 mu_params = { | 408 mu_params = { |
352 | 418 |
353 if experiment["experiment_preset"] == "custom": | 419 if experiment["experiment_preset"] == "custom": |
354 del mu_params["experiment_preset"] | 420 del mu_params["experiment_preset"] |
355 del mu_params["experiment"] | 421 del mu_params["experiment"] |
356 | 422 |
357 global euler_convention | 423 global EULER_CONVENTION |
358 euler_convention = mu_params["euler_convention"] | 424 EULER_CONVENTION = mu_params["euler_convention"] |
359 | 425 |
360 err_found = False | |
361 file_contents = [ | 426 file_contents = [ |
362 build_block("name", [out_file_name.strip().replace(" ", "_")]) | 427 build_block("name", [out_file_name.strip().replace(" ", "_")]) |
363 ] | 428 ] |
364 for keyword, val in mu_params.items(): | 429 |
365 if val and val not in ["None"]: | 430 if parse_dict(parse_func_dict, mu_params, file_contents): |
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) | 431 sys.exit(1) |
381 | 432 |
382 write_file("outfile.in", file_contents) | 433 write_file("outfile.in", file_contents) |
383 | 434 |
384 try: | 435 try: |
385 MuSpinInput(open("outfile.in")) | 436 MuSpinInput(open("outfile.in", encoding="utf-8")) |
386 except Exception as e: | 437 except Exception as exc: # pylint: disable=broad-except |
387 sys.stdout.write( | 438 sys.stdout.write( |
388 "Warning, This created file may not work properly. " | 439 "Warning, This created file may not work properly. Error(s) " |
389 "Error(s) encountered when trying to parse the file : {0}".format( | 440 f"encountered when trying to parse the file : {str(exc)}" |
390 str(e) | |
391 ) | |
392 ) | 441 ) |
393 sys.exit(1) | 442 sys.exit(1) |
394 | 443 |
395 | 444 |
396 if __name__ == "__main__": | 445 if __name__ == "__main__": |