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() | 
