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__":