pyerrors.input.sfcf
1import fnmatch 2import itertools 3import os 4import re 5import warnings 6 7import numpy as np # Thinly-wrapped numpy 8 9from ..obs import Obs 10from .utils import check_idl, sort_names 11 12sep = "/" 13 14 15def read_sfcf(path, prefix, name, quarks='.*', corr_type="bi", noffset=0, wf=0, wf2=0, version="1.0c", cfg_separator="n", cfg_func=None, silent=False, **kwargs): 16 """Read sfcf files from given folder structure. 17 18 Parameters 19 ---------- 20 path : str 21 Path to the sfcf files. 22 prefix : str 23 Prefix of the sfcf files. 24 name : str 25 Name of the correlation function to read. 26 quarks : str 27 Label of the quarks used in the sfcf input file. e.g. "quark quark" 28 for version 0.0 this does NOT need to be given with the typical " - " 29 that is present in the output file, 30 this is done automatically for this version 31 corr_type : str 32 Type of correlation function to read. Can be 33 - 'bi' for boundary-inner 34 - 'bb' for boundary-boundary 35 - 'bib' for boundary-inner-boundary 36 noffset : int 37 Offset of the source (only relevant when wavefunctions are used) 38 wf : int 39 ID of wave function 40 wf2 : int 41 ID of the second wavefunction 42 (only relevant for boundary-to-boundary correlation functions) 43 im : bool 44 if True, read imaginary instead of real part 45 of the correlation function. 46 names : list 47 Alternative labeling for replicas/ensembles. 48 Has to have the appropriate length 49 ens_name : str 50 replaces the name of the ensemble 51 version: str 52 version of SFCF, with which the measurement was done. 53 if the compact output option (-c) was specified, 54 append a "c" to the version (e.g. "1.0c") 55 if the append output option (-a) was specified, 56 append an "a" to the version 57 cfg_separator : str 58 String that separates the ensemble identifier from the configuration number (default 'n'). 59 replica: list 60 list of replica to be read, default is all 61 files: list 62 list of files to be read per replica, default is all. 63 for non-compact output format, hand the folders to be read here. 64 check_configs: list[list[int]] 65 list of list of supposed configs, eg. [range(1,1000)] 66 for one replicum with 1000 configs 67 68 Returns 69 ------- 70 result: list[Obs] 71 list of Observables with length T, observable per timeslice. 72 bb-type correlators have length 1. 73 """ 74 ret = read_sfcf_multi(path, prefix, [name], quarks_list=[quarks], corr_type_list=[corr_type], 75 noffset_list=[noffset], wf_list=[wf], wf2_list=[wf2], version=version, 76 cfg_separator=cfg_separator, cfg_func=cfg_func, silent=silent, **kwargs) 77 return ret[name][quarks][str(noffset)][str(wf)][str(wf2)] 78 79 80def read_sfcf_multi(path, prefix, name_list, quarks_list=None, corr_type_list=None, noffset_list=None, wf_list=None, wf2_list=None, version="1.0c", cfg_separator="n", cfg_func=None, silent=False, keyed_out=False, **kwargs): 81 """Read sfcf files from given folder structure. 82 83 Parameters 84 ---------- 85 path : str 86 Path to the sfcf files. 87 prefix : str 88 Prefix of the sfcf files. 89 name : str 90 Name of the correlation function to read. 91 quarks_list : list[str] 92 Label of the quarks used in the sfcf input file. e.g. "quark quark" 93 for version 0.0 this does NOT need to be given with the typical " - " 94 that is present in the output file, 95 this is done automatically for this version 96 corr_type_list : list[str] 97 Type of correlation function to read. Can be 98 - 'bi' for boundary-inner 99 - 'bb' for boundary-boundary 100 - 'bib' for boundary-inner-boundary 101 noffset_list : list[int] 102 Offset of the source (only relevant when wavefunctions are used) 103 wf_list : int 104 ID of wave function 105 wf2_list : list[int] 106 ID of the second wavefunction 107 (only relevant for boundary-to-boundary correlation functions) 108 im : bool 109 if True, read imaginary instead of real part 110 of the correlation function. 111 names : list 112 Alternative labeling for replicas/ensembles. 113 Has to have the appropriate length 114 ens_name : str 115 replaces the name of the ensemble 116 version: str 117 version of SFCF, with which the measurement was done. 118 if the compact output option (-c) was specified, 119 append a "c" to the version (e.g. "1.0c") 120 if the append output option (-a) was specified, 121 append an "a" to the version 122 cfg_separator : str 123 String that separates the ensemble identifier from the configuration number (default 'n'). 124 replica: list 125 list of replica to be read, default is all 126 files: list[list[int]] 127 list of files to be read per replica, default is all. 128 for non-compact output format, hand the folders to be read here. 129 check_configs: list[list[int]] 130 list of list of supposed configs, eg. [range(1,1000)] 131 for one replicum with 1000 configs 132 rep_string: str 133 Separator of ensemble name and replicum. Example: In "ensAr0", "r" would be the separator string. 134 Returns 135 ------- 136 result: dict[list[Obs]] 137 dict with one of the following properties: 138 if keyed_out: 139 dict[key] = list[Obs] 140 where key has the form name/quarks/offset/wf/wf2 141 if not keyed_out: 142 dict[name][quarks][offset][wf][wf2] = list[Obs] 143 """ 144 145 if quarks_list is None: 146 quarks_list = ['.*'] 147 if corr_type_list is None: 148 corr_type_list = ['bi'] 149 if noffset_list is None: 150 noffset_list = [0] 151 if wf_list is None: 152 wf_list = [0] 153 if wf2_list is None: 154 wf2_list = [0] 155 156 if kwargs.get('im'): 157 im = 1 158 part = 'imaginary' 159 else: 160 im = 0 161 part = 'real' 162 163 known_versions = ["0.0", "1.0", "2.0", "1.0c", "2.0c", "1.0a", "2.0a"] 164 165 if version not in known_versions: 166 raise ValueError("This version is not known!") 167 if (version[-1] == "c"): 168 appended = False 169 compact = True 170 version = version[:-1] 171 elif (version[-1] == "a"): 172 appended = True 173 compact = False 174 version = version[:-1] 175 else: 176 compact = False 177 appended = False 178 ls = [] 179 if "replica" in kwargs: 180 ls = kwargs.get("replica") 181 else: 182 for (_dirpath, dirnames, filenames) in os.walk(path): 183 if not appended: 184 ls.extend(dirnames) 185 else: 186 ls.extend(filenames) 187 break 188 if not ls: 189 raise FileNotFoundError('Error, directory not found') 190 # Exclude folders with different names 191 for exc in ls: 192 if not fnmatch.fnmatch(exc, prefix + '*'): 193 ls = list(set(ls) - set([exc])) 194 195 if not appended: 196 ls = sort_names(ls) 197 replica = len(ls) 198 199 else: 200 replica = len([file.split(".")[-1] for file in ls]) // len(set([file.split(".")[-1] for file in ls])) 201 if replica == 0: 202 raise FileNotFoundError('No replica found in directory') 203 if not silent: 204 print('Read', part, 'part of', name_list, 'from', prefix[:-1], ',', replica, 'replica') 205 206 if 'names' in kwargs: 207 new_names = kwargs.get('names') 208 if len(new_names) != len(set(new_names)): 209 raise ValueError("names are not unique!") 210 if len(new_names) != replica: 211 raise ValueError(f'names should have the length {replica}') 212 213 else: 214 ens_name = kwargs.get("ens_name") 215 if not appended: 216 new_names = _get_rep_names(ls, ens_name, rep_sep=(kwargs.get('rep_string', 'r'))) 217 else: 218 new_names = _get_appended_rep_names(ls, prefix, name_list[0], ens_name, rep_sep=(kwargs.get('rep_string', 'r'))) 219 new_names = sort_names(new_names) 220 221 idl = [] 222 223 noffset_list = [str(x) for x in noffset_list] 224 wf_list = [str(x) for x in wf_list] 225 wf2_list = [str(x) for x in wf2_list] 226 227 # setup dict structures 228 intern = {} 229 for name, corr_type in zip(name_list, corr_type_list, strict=True): 230 intern[name] = {} 231 b2b, single = _extract_corr_type(corr_type) 232 intern[name]["b2b"] = b2b 233 intern[name]["single"] = single 234 intern[name]["spec"] = {} 235 for quarks in quarks_list: 236 intern[name]["spec"][quarks] = {} 237 for off in noffset_list: 238 intern[name]["spec"][quarks][off] = {} 239 for w in wf_list: 240 intern[name]["spec"][quarks][off][w] = {} 241 if b2b: 242 for w2 in wf2_list: 243 intern[name]["spec"][quarks][off][w][w2] = {} 244 intern[name]["spec"][quarks][off][w][w2]["pattern"] = _make_pattern(version, name, off, w, w2, intern[name]['b2b'], quarks) 245 else: 246 intern[name]["spec"][quarks][off][w]["0"] = {} 247 intern[name]["spec"][quarks][off][w]["0"]["pattern"] = _make_pattern(version, name, off, w, 0, intern[name]['b2b'], quarks) 248 249 internal_ret_dict = {} 250 needed_keys = [] 251 for name, corr_type in zip(name_list, corr_type_list, strict=True): 252 b2b, single = _extract_corr_type(corr_type) 253 if b2b: 254 needed_keys.extend(_lists2key([name], quarks_list, noffset_list, wf_list, wf2_list)) 255 else: 256 needed_keys.extend(_lists2key([name], quarks_list, noffset_list, wf_list, ["0"])) 257 258 for key in needed_keys: 259 internal_ret_dict[key] = [] 260 261 def _default_idl_func(cfg_string, cfg_sep): 262 return int(cfg_string.split(cfg_sep)[-1]) 263 264 if cfg_func is None: 265 print("Default idl function in use.") 266 cfg_func = _default_idl_func 267 cfg_func_args = [cfg_separator] 268 else: 269 cfg_func_args = kwargs.get("cfg_func_args", []) 270 271 if not appended: 272 for i, item in enumerate(ls): 273 rep_path = path + '/' + item 274 if "files" in kwargs: 275 files = kwargs.get("files") 276 if isinstance(files, list): 277 if all(isinstance(f, list) for f in files): 278 files = files[i] 279 elif not all(isinstance(f, str) for f in files): 280 raise TypeError("files has to be of type list[list[str]] or list[str]!") 281 else: 282 raise TypeError("files has to be of type list[list[str]] or list[str]!") 283 284 else: 285 files = [] 286 sub_ls = _find_files(rep_path, prefix, compact, files) 287 rep_idl = [] 288 no_cfg = len(sub_ls) 289 for cfg in sub_ls: 290 try: 291 if compact: 292 rep_idl.append(cfg_func(cfg, *cfg_func_args)) 293 else: 294 rep_idl.append(int(cfg[3:])) 295 except Exception as err: 296 raise Exception("Couldn't parse idl from directory, problem with file " + cfg) from err 297 rep_idl.sort() 298 # maybe there is a better way to print the idls 299 if not silent: 300 print(item, ':', no_cfg, ' configurations') 301 idl.append(rep_idl) 302 # here we have found all the files we need to look into. 303 if i == 0: 304 if version != "0.0" and compact: 305 file = path + '/' + item + '/' + sub_ls[0] 306 for name_index, name in enumerate(name_list): 307 if version == "0.0" or not compact: 308 file = path + '/' + item + '/' + sub_ls[0] + '/' + name 309 if corr_type_list[name_index] == 'bi': 310 name_keys = _lists2key(quarks_list, noffset_list, wf_list, ["0"]) 311 else: 312 name_keys = _lists2key(quarks_list, noffset_list, wf_list, wf2_list) 313 for key in name_keys: 314 specs = _key2specs(key) 315 quarks = specs[0] 316 off = specs[1] 317 w = specs[2] 318 w2 = specs[3] 319 # here, we want to find the place within the file, 320 # where the correlator we need is stored. 321 # to do so, the pattern needed is put together 322 # from the input values 323 start_read, T = _find_correlator(file, version, intern[name]["spec"][quarks][str(off)][str(w)][str(w2)]["pattern"], intern[name]['b2b'], silent=silent) 324 intern[name]["spec"][quarks][str(off)][str(w)][str(w2)]["start"] = start_read 325 intern[name]["T"] = T 326 # preparing the datastructure 327 # the correlators get parsed into... 328 deltas = [] 329 for _j in range(intern[name]["T"]): 330 deltas.append([]) 331 internal_ret_dict[sep.join([name, key])] = deltas 332 333 if compact: 334 rep_deltas = _read_compact_rep(path, item, sub_ls, intern, needed_keys, im) 335 for key in needed_keys: 336 name = _key2specs(key)[0] 337 for t in range(intern[name]["T"]): 338 internal_ret_dict[key][t].append(rep_deltas[key][t]) 339 else: 340 for key in needed_keys: 341 rep_data = [] 342 name = _key2specs(key)[0] 343 for subitem in sub_ls: 344 cfg_path = path + '/' + item + '/' + subitem 345 file_data = _read_o_file(cfg_path, name, needed_keys, intern, version, im) 346 rep_data.append(file_data) 347 for t in range(intern[name]["T"]): 348 internal_ret_dict[key][t].append([]) 349 for cfg in range(no_cfg): 350 internal_ret_dict[key][t][i].append(rep_data[cfg][key][t]) 351 else: 352 for key in needed_keys: 353 specs = _key2specs(key) 354 name = specs[0] 355 quarks = specs[1] 356 off = specs[2] 357 w = specs[3] 358 w2 = specs[4] 359 if "files" in kwargs: 360 if isinstance(kwargs.get("files"), list) and all(isinstance(f, str) for f in kwargs.get("files")): 361 name_ls = kwargs.get("files") 362 else: 363 raise TypeError("In append mode, files has to be of type list[str]!") 364 else: 365 name_ls = ls 366 for exc in name_ls: 367 if not fnmatch.fnmatch(exc, prefix + '*.' + name): 368 name_ls = list(set(name_ls) - set([exc])) 369 name_ls = sort_names(name_ls) 370 pattern = intern[name]['spec'][quarks][off][w][w2]['pattern'] 371 deltas = [] 372 for rep, file in enumerate(name_ls): 373 rep_idl = [] 374 filename = path + '/' + file 375 T, rep_idl, rep_data = _read_append_rep(filename, pattern, intern[name]['b2b'], im, intern[name]['single'], cfg_func, cfg_func_args) 376 if rep == 0: 377 intern[name]['T'] = T 378 for _ in range(intern[name]['T']): 379 deltas.append([]) 380 for t in range(intern[name]['T']): 381 deltas[t].append(rep_data[t]) 382 internal_ret_dict[key] = deltas 383 if name == name_list[0]: 384 idl.append(rep_idl) 385 386 if kwargs.get("check_configs") is True: 387 if not silent: 388 print("Checking for missing configs...") 389 che = kwargs.get("check_configs") 390 if not (len(che) == len(idl)): 391 raise ValueError("check_configs has to be the same length as replica!") 392 for r in range(len(idl)): 393 if not silent: 394 print("checking " + new_names[r]) 395 check_idl(idl[r], che[r]) 396 if not silent: 397 print("Done") 398 399 result_dict = {} 400 if keyed_out: 401 for key in needed_keys: 402 name = _key2specs(key)[0] 403 result = [] 404 for t in range(intern[name]["T"]): 405 result.append(Obs(internal_ret_dict[key][t], new_names, idl=idl)) 406 result_dict[key] = result 407 else: 408 for name, corr_type in zip(name_list, corr_type_list, strict=True): 409 result_dict[name] = {} 410 for quarks in quarks_list: 411 result_dict[name][quarks] = {} 412 for off in noffset_list: 413 result_dict[name][quarks][off] = {} 414 for w in wf_list: 415 result_dict[name][quarks][off][w] = {} 416 if corr_type != 'bi': 417 for w2 in wf2_list: 418 key = _specs2key(name, quarks, off, w, w2) 419 result = [] 420 for t in range(intern[name]["T"]): 421 result.append(Obs(internal_ret_dict[key][t], new_names, idl=idl)) 422 result_dict[name][quarks][str(off)][str(w)][str(w2)] = result 423 else: 424 key = _specs2key(name, quarks, off, w, "0") 425 result = [] 426 for t in range(intern[name]["T"]): 427 result.append(Obs(internal_ret_dict[key][t], new_names, idl=idl)) 428 result_dict[name][quarks][str(off)][str(w)][str(0)] = result 429 return result_dict 430 431 432def _lists2key(*lists): 433 keys = [] 434 for tup in itertools.product(*lists): 435 keys.append(sep.join(tup)) 436 return keys 437 438 439def _key2specs(key): 440 return key.split(sep) 441 442 443def _specs2key(*specs): 444 return sep.join(specs) 445 446 447def _read_o_file(cfg_path, name, needed_keys, intern, version, im): 448 return_vals = {} 449 for key in needed_keys: 450 file = cfg_path + '/' + name 451 specs = _key2specs(key) 452 if specs[0] == name: 453 with open(file) as fp: 454 lines = fp.readlines() 455 quarks = specs[1] 456 off = specs[2] 457 w = specs[3] 458 w2 = specs[4] 459 T = intern[name]["T"] 460 start_read = intern[name]["spec"][quarks][off][w][w2]["start"] 461 deltas = [] 462 for line in lines[start_read:start_read + T]: 463 floats = list(map(float, line.split())) 464 if version == "0.0": 465 deltas.append(floats[im - intern[name]["single"]]) 466 else: 467 deltas.append(floats[1 + im - intern[name]["single"]]) 468 return_vals[key] = deltas 469 return return_vals 470 471 472def _extract_corr_type(corr_type): 473 if corr_type == 'bb': 474 b2b = True 475 single = True 476 elif corr_type == 'bib': 477 b2b = True 478 single = False 479 else: 480 b2b = False 481 single = False 482 return b2b, single 483 484 485def _find_files(rep_path, prefix, compact, files=None): 486 if files is None: 487 files = [] 488 sub_ls = [] 489 if not files == []: 490 files.sort(key=lambda x: int(re.findall(r'\d+', x)[-1])) 491 else: 492 for (_dirpath, dirnames, filenames) in os.walk(rep_path): 493 if compact: 494 sub_ls.extend(filenames) 495 else: 496 sub_ls.extend(dirnames) 497 break 498 if compact: 499 for exc in sub_ls: 500 if not fnmatch.fnmatch(exc, prefix + '*'): 501 sub_ls = list(set(sub_ls) - set([exc])) 502 sub_ls.sort(key=lambda x: int(re.findall(r'\d+', x)[-1])) 503 else: 504 for exc in sub_ls: 505 if not fnmatch.fnmatch(exc, 'cfg*'): 506 sub_ls = list(set(sub_ls) - set([exc])) 507 sub_ls.sort(key=lambda x: int(x[3:])) 508 files = sub_ls 509 if len(files) == 0: 510 raise FileNotFoundError("Did not find files in", rep_path, "with prefix", prefix, "and the given structure.") 511 return files 512 513 514def _make_pattern(version, name, noffset, wf, wf2, b2b, quarks): 515 if version == "0.0": 516 pattern = "# " + name + " : offset " + str(noffset) + ", wf " + str(wf) 517 if b2b: 518 pattern += ", wf_2 " + str(wf2) 519 qs = quarks.split(" ") 520 pattern += " : " + qs[0] + " - " + qs[1] 521 else: 522 pattern = 'name ' + name + '\nquarks ' + quarks + '\noffset ' + str(noffset) + '\nwf ' + str(wf) 523 if b2b: 524 pattern += '\nwf_2 ' + str(wf2) 525 return pattern 526 527 528def _find_correlator(file_name, version, pattern, b2b, silent=False): 529 T = 0 530 531 with open(file_name) as my_file: 532 533 content = my_file.read() 534 match = re.search(pattern, content) 535 if match: 536 if version == "0.0": 537 start_read = content.count('\n', 0, match.start()) + 1 538 T = content.count('\n', start_read) 539 else: 540 start_read = content.count('\n', 0, match.start()) + 5 + b2b 541 end_match = re.search(r'\n\s*\n', content[match.start():]) 542 T = content[match.start():].count('\n', 0, end_match.start()) - 4 - b2b 543 if not T > 0: 544 raise ValueError("Correlator with pattern\n" + pattern + "\nis empty!") 545 if not silent: 546 print(T, 'entries, starting to read in line', start_read) 547 548 else: 549 raise ValueError('Correlator with pattern\n' + pattern + '\nnot found.') 550 551 return start_read, T 552 553 554def _read_compact_file(rep_path, cfg_file, intern, needed_keys, im): 555 return_vals = {} 556 with open(rep_path + cfg_file) as fp: 557 lines = fp.readlines() 558 for key in needed_keys: 559 keys = _key2specs(key) 560 name = keys[0] 561 quarks = keys[1] 562 off = keys[2] 563 w = keys[3] 564 w2 = keys[4] 565 566 T = intern[name]["T"] 567 start_read = intern[name]["spec"][quarks][off][w][w2]["start"] 568 # check, if the correlator is in fact 569 # printed completely 570 if (start_read + T + 1 > len(lines)): 571 raise Exception("EOF before end of correlator data! Maybe " + rep_path + cfg_file + " is corrupted?") 572 corr_lines = lines[start_read - 6: start_read + T] 573 t_vals = [] 574 575 if corr_lines[1 - intern[name]["b2b"]].strip() != 'name ' + name: 576 raise Exception('Wrong format in file', cfg_file) 577 578 for k in range(6, T + 6): 579 floats = list(map(float, corr_lines[k].split())) 580 t_vals.append(floats[-2:][im]) 581 return_vals[key] = t_vals 582 return return_vals 583 584 585def _read_compact_rep(path, rep, sub_ls, intern, needed_keys, im): 586 rep_path = path + '/' + rep + '/' 587 no_cfg = len(sub_ls) 588 589 return_vals = {} 590 for key in needed_keys: 591 name = _key2specs(key)[0] 592 deltas = [] 593 for _ in range(intern[name]["T"]): 594 deltas.append(np.zeros(no_cfg)) 595 return_vals[key] = deltas 596 597 for cfg in range(no_cfg): 598 cfg_file = sub_ls[cfg] 599 cfg_data = _read_compact_file(rep_path, cfg_file, intern, needed_keys, im) 600 for key in needed_keys: 601 name = _key2specs(key)[0] 602 for t in range(intern[name]["T"]): 603 return_vals[key][t][cfg] = cfg_data[key][t] 604 return return_vals 605 606 607def _read_chunk_data(chunk, start_read, T, corr_line, b2b, pattern, im, single): 608 found_pat = "" 609 data = [] 610 for li in chunk[corr_line + 1:corr_line + 6 + b2b]: 611 found_pat += li 612 if re.search(pattern, found_pat): 613 for _t, line in enumerate(chunk[start_read:start_read + T]): 614 floats = list(map(float, line.split())) 615 data.append(floats[im + 1 - single]) 616 return data 617 618 619def _check_append_rep(content, start_list): 620 data_len_list = [] 621 header_len_list = [] 622 has_regular_len_heads = True 623 for chunk_num in range(len(start_list)): 624 start = start_list[chunk_num] 625 if chunk_num == len(start_list) - 1: 626 stop = len(content) 627 else: 628 stop = start_list[chunk_num + 1] 629 chunk = content[start:stop] 630 for linenumber, line in enumerate(chunk): 631 if line.startswith("[correlator]"): 632 header_len = linenumber 633 break 634 header_len_list.append(header_len) 635 data_len_list.append(len(chunk) - header_len) 636 637 if len(set(header_len_list)) > 1: 638 warnings.warn("Not all headers have the same length. Data parts do.", stacklevel=2) 639 has_regular_len_heads = False 640 641 if len(set(data_len_list)) > 1: 642 raise Exception("Irregularities in file structure found, not all run data are of the same output length") 643 return has_regular_len_heads 644 645 646def _read_chunk_structure(chunk, pattern, b2b): 647 start_read = 0 648 for linenumber, line in enumerate(chunk): 649 if line.startswith("gauge_name"): 650 gauge_line = linenumber 651 elif line.startswith("[correlator]"): 652 corr_line = linenumber 653 found_pat = "" 654 for li in chunk[corr_line + 1: corr_line + 6 + b2b]: 655 found_pat += li 656 if re.search(pattern, found_pat): 657 start_read = corr_line + 7 + b2b 658 break 659 if start_read == 0: 660 raise ValueError("Did not find pattern\n", pattern) 661 endline = corr_line + 6 + b2b 662 while not chunk[endline] == "\n": 663 endline += 1 664 T = endline - start_read 665 return gauge_line, corr_line, start_read, T 666 667 668def _read_append_rep(filename, pattern, b2b, im, single, idl_func, cfg_func_args): 669 with open(filename) as fp: 670 content = fp.readlines() 671 chunk_start_lines = [] 672 for linenumber, line in enumerate(content): 673 if "[run]" in line: 674 chunk_start_lines.append(linenumber) 675 has_regular_len_heads = _check_append_rep(content, chunk_start_lines) 676 if has_regular_len_heads: 677 chunk = content[:chunk_start_lines[1]] 678 try: 679 gauge_line, corr_line, start_read, T = _read_chunk_structure(chunk, pattern, b2b) 680 except ValueError as err: 681 raise ValueError("Did not find pattern\n", pattern, "\nin\n", filename, "lines", 1, "to", chunk_start_lines[1] + 1) from err 682 # if has_regular_len_heads is true, all other chunks should follow the same structure 683 rep_idl = [] 684 rep_data = [] 685 686 for chunk_num in range(len(chunk_start_lines)): 687 start = chunk_start_lines[chunk_num] 688 if chunk_num == len(chunk_start_lines) - 1: 689 stop = len(content) 690 else: 691 stop = chunk_start_lines[chunk_num + 1] 692 chunk = content[start:stop] 693 if not has_regular_len_heads: 694 gauge_line, corr_line, start_read, T = _read_chunk_structure(chunk, pattern, b2b) 695 try: 696 idl = idl_func(chunk[gauge_line], *cfg_func_args) 697 except Exception as err: 698 raise Exception("Couldn't parse idl from file", filename, ", problem with chunk of lines", start + 1, "to", stop + 1) from err 699 data = _read_chunk_data(chunk, start_read, T, corr_line, b2b, pattern, im, single) 700 rep_idl.append(idl) 701 rep_data.append(data) 702 703 data = [] 704 705 for t in range(T): 706 data.append([]) 707 for c in range(len(rep_data)): 708 data[t].append(rep_data[c][t]) 709 return T, rep_idl, data 710 711 712def _get_rep_names(ls, ens_name=None, rep_sep='r'): 713 new_names = [] 714 for entry in ls: 715 try: 716 idx = entry.index(rep_sep) 717 except Exception as err: 718 raise Exception("Automatic recognition of replicum failed, please enter the key word 'names'.") from err 719 720 if ens_name: 721 new_names.append(ens_name + '|' + entry[idx:]) 722 else: 723 new_names.append(entry[:idx] + '|' + entry[idx:]) 724 return new_names 725 726 727def _get_appended_rep_names(ls, prefix, name, ens_name=None, rep_sep='r'): 728 new_names = [] 729 for exc in ls: 730 if not fnmatch.fnmatch(exc, prefix + '*.' + name): 731 ls = list(set(ls) - set([exc])) 732 ls.sort(key=lambda x: int(re.findall(r'\d+', x)[-1])) 733 for entry in ls: 734 myentry = entry[:-len(name) - 1] 735 try: 736 idx = myentry.index(rep_sep) 737 except Exception as err: 738 raise Exception("Automatic recognition of replicum failed, please enter the key word 'names'.") from err 739 740 if ens_name: 741 new_names.append(ens_name + '|' + entry[idx:]) 742 else: 743 new_names.append(myentry[:idx] + '|' + myentry[idx:]) 744 return new_names
sep =
'/'
def
read_sfcf( path, prefix, name, quarks='.*', corr_type='bi', noffset=0, wf=0, wf2=0, version='1.0c', cfg_separator='n', cfg_func=None, silent=False, **kwargs):
16def read_sfcf(path, prefix, name, quarks='.*', corr_type="bi", noffset=0, wf=0, wf2=0, version="1.0c", cfg_separator="n", cfg_func=None, silent=False, **kwargs): 17 """Read sfcf files from given folder structure. 18 19 Parameters 20 ---------- 21 path : str 22 Path to the sfcf files. 23 prefix : str 24 Prefix of the sfcf files. 25 name : str 26 Name of the correlation function to read. 27 quarks : str 28 Label of the quarks used in the sfcf input file. e.g. "quark quark" 29 for version 0.0 this does NOT need to be given with the typical " - " 30 that is present in the output file, 31 this is done automatically for this version 32 corr_type : str 33 Type of correlation function to read. Can be 34 - 'bi' for boundary-inner 35 - 'bb' for boundary-boundary 36 - 'bib' for boundary-inner-boundary 37 noffset : int 38 Offset of the source (only relevant when wavefunctions are used) 39 wf : int 40 ID of wave function 41 wf2 : int 42 ID of the second wavefunction 43 (only relevant for boundary-to-boundary correlation functions) 44 im : bool 45 if True, read imaginary instead of real part 46 of the correlation function. 47 names : list 48 Alternative labeling for replicas/ensembles. 49 Has to have the appropriate length 50 ens_name : str 51 replaces the name of the ensemble 52 version: str 53 version of SFCF, with which the measurement was done. 54 if the compact output option (-c) was specified, 55 append a "c" to the version (e.g. "1.0c") 56 if the append output option (-a) was specified, 57 append an "a" to the version 58 cfg_separator : str 59 String that separates the ensemble identifier from the configuration number (default 'n'). 60 replica: list 61 list of replica to be read, default is all 62 files: list 63 list of files to be read per replica, default is all. 64 for non-compact output format, hand the folders to be read here. 65 check_configs: list[list[int]] 66 list of list of supposed configs, eg. [range(1,1000)] 67 for one replicum with 1000 configs 68 69 Returns 70 ------- 71 result: list[Obs] 72 list of Observables with length T, observable per timeslice. 73 bb-type correlators have length 1. 74 """ 75 ret = read_sfcf_multi(path, prefix, [name], quarks_list=[quarks], corr_type_list=[corr_type], 76 noffset_list=[noffset], wf_list=[wf], wf2_list=[wf2], version=version, 77 cfg_separator=cfg_separator, cfg_func=cfg_func, silent=silent, **kwargs) 78 return ret[name][quarks][str(noffset)][str(wf)][str(wf2)]
Read sfcf files from given folder structure.
Parameters
- path (str): Path to the sfcf files.
- prefix (str): Prefix of the sfcf files.
- name (str): Name of the correlation function to read.
- quarks (str): Label of the quarks used in the sfcf input file. e.g. "quark quark" for version 0.0 this does NOT need to be given with the typical " - " that is present in the output file, this is done automatically for this version
- corr_type (str):
Type of correlation function to read. Can be
- 'bi' for boundary-inner
- 'bb' for boundary-boundary
- 'bib' for boundary-inner-boundary
- noffset (int): Offset of the source (only relevant when wavefunctions are used)
- wf (int): ID of wave function
- wf2 (int): ID of the second wavefunction (only relevant for boundary-to-boundary correlation functions)
- im (bool): if True, read imaginary instead of real part of the correlation function.
- names (list): Alternative labeling for replicas/ensembles. Has to have the appropriate length
- ens_name (str): replaces the name of the ensemble
- version (str): version of SFCF, with which the measurement was done. if the compact output option (-c) was specified, append a "c" to the version (e.g. "1.0c") if the append output option (-a) was specified, append an "a" to the version
- cfg_separator (str): String that separates the ensemble identifier from the configuration number (default 'n').
- replica (list): list of replica to be read, default is all
- files (list): list of files to be read per replica, default is all. for non-compact output format, hand the folders to be read here.
- check_configs (list[list[int]]): list of list of supposed configs, eg. [range(1,1000)] for one replicum with 1000 configs
Returns
- result (list[Obs]): list of Observables with length T, observable per timeslice. bb-type correlators have length 1.
def
read_sfcf_multi( path, prefix, name_list, quarks_list=None, corr_type_list=None, noffset_list=None, wf_list=None, wf2_list=None, version='1.0c', cfg_separator='n', cfg_func=None, silent=False, keyed_out=False, **kwargs):
81def read_sfcf_multi(path, prefix, name_list, quarks_list=None, corr_type_list=None, noffset_list=None, wf_list=None, wf2_list=None, version="1.0c", cfg_separator="n", cfg_func=None, silent=False, keyed_out=False, **kwargs): 82 """Read sfcf files from given folder structure. 83 84 Parameters 85 ---------- 86 path : str 87 Path to the sfcf files. 88 prefix : str 89 Prefix of the sfcf files. 90 name : str 91 Name of the correlation function to read. 92 quarks_list : list[str] 93 Label of the quarks used in the sfcf input file. e.g. "quark quark" 94 for version 0.0 this does NOT need to be given with the typical " - " 95 that is present in the output file, 96 this is done automatically for this version 97 corr_type_list : list[str] 98 Type of correlation function to read. Can be 99 - 'bi' for boundary-inner 100 - 'bb' for boundary-boundary 101 - 'bib' for boundary-inner-boundary 102 noffset_list : list[int] 103 Offset of the source (only relevant when wavefunctions are used) 104 wf_list : int 105 ID of wave function 106 wf2_list : list[int] 107 ID of the second wavefunction 108 (only relevant for boundary-to-boundary correlation functions) 109 im : bool 110 if True, read imaginary instead of real part 111 of the correlation function. 112 names : list 113 Alternative labeling for replicas/ensembles. 114 Has to have the appropriate length 115 ens_name : str 116 replaces the name of the ensemble 117 version: str 118 version of SFCF, with which the measurement was done. 119 if the compact output option (-c) was specified, 120 append a "c" to the version (e.g. "1.0c") 121 if the append output option (-a) was specified, 122 append an "a" to the version 123 cfg_separator : str 124 String that separates the ensemble identifier from the configuration number (default 'n'). 125 replica: list 126 list of replica to be read, default is all 127 files: list[list[int]] 128 list of files to be read per replica, default is all. 129 for non-compact output format, hand the folders to be read here. 130 check_configs: list[list[int]] 131 list of list of supposed configs, eg. [range(1,1000)] 132 for one replicum with 1000 configs 133 rep_string: str 134 Separator of ensemble name and replicum. Example: In "ensAr0", "r" would be the separator string. 135 Returns 136 ------- 137 result: dict[list[Obs]] 138 dict with one of the following properties: 139 if keyed_out: 140 dict[key] = list[Obs] 141 where key has the form name/quarks/offset/wf/wf2 142 if not keyed_out: 143 dict[name][quarks][offset][wf][wf2] = list[Obs] 144 """ 145 146 if quarks_list is None: 147 quarks_list = ['.*'] 148 if corr_type_list is None: 149 corr_type_list = ['bi'] 150 if noffset_list is None: 151 noffset_list = [0] 152 if wf_list is None: 153 wf_list = [0] 154 if wf2_list is None: 155 wf2_list = [0] 156 157 if kwargs.get('im'): 158 im = 1 159 part = 'imaginary' 160 else: 161 im = 0 162 part = 'real' 163 164 known_versions = ["0.0", "1.0", "2.0", "1.0c", "2.0c", "1.0a", "2.0a"] 165 166 if version not in known_versions: 167 raise ValueError("This version is not known!") 168 if (version[-1] == "c"): 169 appended = False 170 compact = True 171 version = version[:-1] 172 elif (version[-1] == "a"): 173 appended = True 174 compact = False 175 version = version[:-1] 176 else: 177 compact = False 178 appended = False 179 ls = [] 180 if "replica" in kwargs: 181 ls = kwargs.get("replica") 182 else: 183 for (_dirpath, dirnames, filenames) in os.walk(path): 184 if not appended: 185 ls.extend(dirnames) 186 else: 187 ls.extend(filenames) 188 break 189 if not ls: 190 raise FileNotFoundError('Error, directory not found') 191 # Exclude folders with different names 192 for exc in ls: 193 if not fnmatch.fnmatch(exc, prefix + '*'): 194 ls = list(set(ls) - set([exc])) 195 196 if not appended: 197 ls = sort_names(ls) 198 replica = len(ls) 199 200 else: 201 replica = len([file.split(".")[-1] for file in ls]) // len(set([file.split(".")[-1] for file in ls])) 202 if replica == 0: 203 raise FileNotFoundError('No replica found in directory') 204 if not silent: 205 print('Read', part, 'part of', name_list, 'from', prefix[:-1], ',', replica, 'replica') 206 207 if 'names' in kwargs: 208 new_names = kwargs.get('names') 209 if len(new_names) != len(set(new_names)): 210 raise ValueError("names are not unique!") 211 if len(new_names) != replica: 212 raise ValueError(f'names should have the length {replica}') 213 214 else: 215 ens_name = kwargs.get("ens_name") 216 if not appended: 217 new_names = _get_rep_names(ls, ens_name, rep_sep=(kwargs.get('rep_string', 'r'))) 218 else: 219 new_names = _get_appended_rep_names(ls, prefix, name_list[0], ens_name, rep_sep=(kwargs.get('rep_string', 'r'))) 220 new_names = sort_names(new_names) 221 222 idl = [] 223 224 noffset_list = [str(x) for x in noffset_list] 225 wf_list = [str(x) for x in wf_list] 226 wf2_list = [str(x) for x in wf2_list] 227 228 # setup dict structures 229 intern = {} 230 for name, corr_type in zip(name_list, corr_type_list, strict=True): 231 intern[name] = {} 232 b2b, single = _extract_corr_type(corr_type) 233 intern[name]["b2b"] = b2b 234 intern[name]["single"] = single 235 intern[name]["spec"] = {} 236 for quarks in quarks_list: 237 intern[name]["spec"][quarks] = {} 238 for off in noffset_list: 239 intern[name]["spec"][quarks][off] = {} 240 for w in wf_list: 241 intern[name]["spec"][quarks][off][w] = {} 242 if b2b: 243 for w2 in wf2_list: 244 intern[name]["spec"][quarks][off][w][w2] = {} 245 intern[name]["spec"][quarks][off][w][w2]["pattern"] = _make_pattern(version, name, off, w, w2, intern[name]['b2b'], quarks) 246 else: 247 intern[name]["spec"][quarks][off][w]["0"] = {} 248 intern[name]["spec"][quarks][off][w]["0"]["pattern"] = _make_pattern(version, name, off, w, 0, intern[name]['b2b'], quarks) 249 250 internal_ret_dict = {} 251 needed_keys = [] 252 for name, corr_type in zip(name_list, corr_type_list, strict=True): 253 b2b, single = _extract_corr_type(corr_type) 254 if b2b: 255 needed_keys.extend(_lists2key([name], quarks_list, noffset_list, wf_list, wf2_list)) 256 else: 257 needed_keys.extend(_lists2key([name], quarks_list, noffset_list, wf_list, ["0"])) 258 259 for key in needed_keys: 260 internal_ret_dict[key] = [] 261 262 def _default_idl_func(cfg_string, cfg_sep): 263 return int(cfg_string.split(cfg_sep)[-1]) 264 265 if cfg_func is None: 266 print("Default idl function in use.") 267 cfg_func = _default_idl_func 268 cfg_func_args = [cfg_separator] 269 else: 270 cfg_func_args = kwargs.get("cfg_func_args", []) 271 272 if not appended: 273 for i, item in enumerate(ls): 274 rep_path = path + '/' + item 275 if "files" in kwargs: 276 files = kwargs.get("files") 277 if isinstance(files, list): 278 if all(isinstance(f, list) for f in files): 279 files = files[i] 280 elif not all(isinstance(f, str) for f in files): 281 raise TypeError("files has to be of type list[list[str]] or list[str]!") 282 else: 283 raise TypeError("files has to be of type list[list[str]] or list[str]!") 284 285 else: 286 files = [] 287 sub_ls = _find_files(rep_path, prefix, compact, files) 288 rep_idl = [] 289 no_cfg = len(sub_ls) 290 for cfg in sub_ls: 291 try: 292 if compact: 293 rep_idl.append(cfg_func(cfg, *cfg_func_args)) 294 else: 295 rep_idl.append(int(cfg[3:])) 296 except Exception as err: 297 raise Exception("Couldn't parse idl from directory, problem with file " + cfg) from err 298 rep_idl.sort() 299 # maybe there is a better way to print the idls 300 if not silent: 301 print(item, ':', no_cfg, ' configurations') 302 idl.append(rep_idl) 303 # here we have found all the files we need to look into. 304 if i == 0: 305 if version != "0.0" and compact: 306 file = path + '/' + item + '/' + sub_ls[0] 307 for name_index, name in enumerate(name_list): 308 if version == "0.0" or not compact: 309 file = path + '/' + item + '/' + sub_ls[0] + '/' + name 310 if corr_type_list[name_index] == 'bi': 311 name_keys = _lists2key(quarks_list, noffset_list, wf_list, ["0"]) 312 else: 313 name_keys = _lists2key(quarks_list, noffset_list, wf_list, wf2_list) 314 for key in name_keys: 315 specs = _key2specs(key) 316 quarks = specs[0] 317 off = specs[1] 318 w = specs[2] 319 w2 = specs[3] 320 # here, we want to find the place within the file, 321 # where the correlator we need is stored. 322 # to do so, the pattern needed is put together 323 # from the input values 324 start_read, T = _find_correlator(file, version, intern[name]["spec"][quarks][str(off)][str(w)][str(w2)]["pattern"], intern[name]['b2b'], silent=silent) 325 intern[name]["spec"][quarks][str(off)][str(w)][str(w2)]["start"] = start_read 326 intern[name]["T"] = T 327 # preparing the datastructure 328 # the correlators get parsed into... 329 deltas = [] 330 for _j in range(intern[name]["T"]): 331 deltas.append([]) 332 internal_ret_dict[sep.join([name, key])] = deltas 333 334 if compact: 335 rep_deltas = _read_compact_rep(path, item, sub_ls, intern, needed_keys, im) 336 for key in needed_keys: 337 name = _key2specs(key)[0] 338 for t in range(intern[name]["T"]): 339 internal_ret_dict[key][t].append(rep_deltas[key][t]) 340 else: 341 for key in needed_keys: 342 rep_data = [] 343 name = _key2specs(key)[0] 344 for subitem in sub_ls: 345 cfg_path = path + '/' + item + '/' + subitem 346 file_data = _read_o_file(cfg_path, name, needed_keys, intern, version, im) 347 rep_data.append(file_data) 348 for t in range(intern[name]["T"]): 349 internal_ret_dict[key][t].append([]) 350 for cfg in range(no_cfg): 351 internal_ret_dict[key][t][i].append(rep_data[cfg][key][t]) 352 else: 353 for key in needed_keys: 354 specs = _key2specs(key) 355 name = specs[0] 356 quarks = specs[1] 357 off = specs[2] 358 w = specs[3] 359 w2 = specs[4] 360 if "files" in kwargs: 361 if isinstance(kwargs.get("files"), list) and all(isinstance(f, str) for f in kwargs.get("files")): 362 name_ls = kwargs.get("files") 363 else: 364 raise TypeError("In append mode, files has to be of type list[str]!") 365 else: 366 name_ls = ls 367 for exc in name_ls: 368 if not fnmatch.fnmatch(exc, prefix + '*.' + name): 369 name_ls = list(set(name_ls) - set([exc])) 370 name_ls = sort_names(name_ls) 371 pattern = intern[name]['spec'][quarks][off][w][w2]['pattern'] 372 deltas = [] 373 for rep, file in enumerate(name_ls): 374 rep_idl = [] 375 filename = path + '/' + file 376 T, rep_idl, rep_data = _read_append_rep(filename, pattern, intern[name]['b2b'], im, intern[name]['single'], cfg_func, cfg_func_args) 377 if rep == 0: 378 intern[name]['T'] = T 379 for _ in range(intern[name]['T']): 380 deltas.append([]) 381 for t in range(intern[name]['T']): 382 deltas[t].append(rep_data[t]) 383 internal_ret_dict[key] = deltas 384 if name == name_list[0]: 385 idl.append(rep_idl) 386 387 if kwargs.get("check_configs") is True: 388 if not silent: 389 print("Checking for missing configs...") 390 che = kwargs.get("check_configs") 391 if not (len(che) == len(idl)): 392 raise ValueError("check_configs has to be the same length as replica!") 393 for r in range(len(idl)): 394 if not silent: 395 print("checking " + new_names[r]) 396 check_idl(idl[r], che[r]) 397 if not silent: 398 print("Done") 399 400 result_dict = {} 401 if keyed_out: 402 for key in needed_keys: 403 name = _key2specs(key)[0] 404 result = [] 405 for t in range(intern[name]["T"]): 406 result.append(Obs(internal_ret_dict[key][t], new_names, idl=idl)) 407 result_dict[key] = result 408 else: 409 for name, corr_type in zip(name_list, corr_type_list, strict=True): 410 result_dict[name] = {} 411 for quarks in quarks_list: 412 result_dict[name][quarks] = {} 413 for off in noffset_list: 414 result_dict[name][quarks][off] = {} 415 for w in wf_list: 416 result_dict[name][quarks][off][w] = {} 417 if corr_type != 'bi': 418 for w2 in wf2_list: 419 key = _specs2key(name, quarks, off, w, w2) 420 result = [] 421 for t in range(intern[name]["T"]): 422 result.append(Obs(internal_ret_dict[key][t], new_names, idl=idl)) 423 result_dict[name][quarks][str(off)][str(w)][str(w2)] = result 424 else: 425 key = _specs2key(name, quarks, off, w, "0") 426 result = [] 427 for t in range(intern[name]["T"]): 428 result.append(Obs(internal_ret_dict[key][t], new_names, idl=idl)) 429 result_dict[name][quarks][str(off)][str(w)][str(0)] = result 430 return result_dict
Read sfcf files from given folder structure.
Parameters
- path (str): Path to the sfcf files.
- prefix (str): Prefix of the sfcf files.
- name (str): Name of the correlation function to read.
- quarks_list (list[str]): Label of the quarks used in the sfcf input file. e.g. "quark quark" for version 0.0 this does NOT need to be given with the typical " - " that is present in the output file, this is done automatically for this version
- corr_type_list (list[str]):
Type of correlation function to read. Can be
- 'bi' for boundary-inner
- 'bb' for boundary-boundary
- 'bib' for boundary-inner-boundary
- noffset_list (list[int]): Offset of the source (only relevant when wavefunctions are used)
- wf_list (int): ID of wave function
- wf2_list (list[int]): ID of the second wavefunction (only relevant for boundary-to-boundary correlation functions)
- im (bool): if True, read imaginary instead of real part of the correlation function.
- names (list): Alternative labeling for replicas/ensembles. Has to have the appropriate length
- ens_name (str): replaces the name of the ensemble
- version (str): version of SFCF, with which the measurement was done. if the compact output option (-c) was specified, append a "c" to the version (e.g. "1.0c") if the append output option (-a) was specified, append an "a" to the version
- cfg_separator (str): String that separates the ensemble identifier from the configuration number (default 'n').
- replica (list): list of replica to be read, default is all
- files (list[list[int]]): list of files to be read per replica, default is all. for non-compact output format, hand the folders to be read here.
- check_configs (list[list[int]]): list of list of supposed configs, eg. [range(1,1000)] for one replicum with 1000 configs
- rep_string (str): Separator of ensemble name and replicum. Example: In "ensAr0", "r" would be the separator string.
Returns
- result (dict[list[Obs]]): dict with one of the following properties: if keyed_out: dict[key] = list[Obs] where key has the form name/quarks/offset/wf/wf2 if not keyed_out: dict[name][quarks][offset][wf][wf2] = list[Obs]