pyerrors.input.openQCD
1import os 2import fnmatch 3import re 4import struct 5import warnings 6import numpy as np # Thinly-wrapped numpy 7import matplotlib.pyplot as plt 8from matplotlib import gridspec 9from ..obs import Obs 10from ..fits import fit_lin 11 12 13def read_rwms(path, prefix, version='2.0', names=None, **kwargs): 14 """Read rwms format from given folder structure. Returns a list of length nrw 15 16 Parameters 17 ---------- 18 path : str 19 path that contains the data files 20 prefix : str 21 all files in path that start with prefix are considered as input files. 22 May be used together postfix to consider only special file endings. 23 Prefix is ignored, if the keyword 'files' is used. 24 version : str 25 version of openQCD, default 2.0 26 names : list 27 list of names that is assigned to the data according according 28 to the order in the file list. Use careful, if you do not provide file names! 29 r_start : list 30 list which contains the first config to be read for each replicum 31 r_stop : list 32 list which contains the last config to be read for each replicum 33 r_step : int 34 integer that defines a fixed step size between two measurements (in units of configs) 35 If not given, r_step=1 is assumed. 36 postfix : str 37 postfix of the file to read, e.g. '.ms1' for openQCD-files 38 files : list 39 list which contains the filenames to be read. No automatic detection of 40 files performed if given. 41 print_err : bool 42 Print additional information that is useful for debugging. 43 """ 44 known_oqcd_versions = ['1.4', '1.6', '2.0'] 45 if not (version in known_oqcd_versions): 46 raise Exception('Unknown openQCD version defined!') 47 print("Working with openQCD version " + version) 48 if 'postfix' in kwargs: 49 postfix = kwargs.get('postfix') 50 else: 51 postfix = '' 52 ls = [] 53 for (dirpath, dirnames, filenames) in os.walk(path): 54 ls.extend(filenames) 55 break 56 57 if not ls: 58 raise Exception(f"Error, directory '{path}' not found") 59 if 'files' in kwargs: 60 ls = kwargs.get('files') 61 else: 62 for exc in ls: 63 if not fnmatch.fnmatch(exc, prefix + '*' + postfix + '.dat'): 64 ls = list(set(ls) - set([exc])) 65 if len(ls) > 1: 66 ls.sort(key=lambda x: int(re.findall(r'\d+', x[len(prefix):])[0])) 67 replica = len(ls) 68 69 if 'r_start' in kwargs: 70 r_start = kwargs.get('r_start') 71 if len(r_start) != replica: 72 raise Exception('r_start does not match number of replicas') 73 r_start = [o if o else None for o in r_start] 74 else: 75 r_start = [None] * replica 76 77 if 'r_stop' in kwargs: 78 r_stop = kwargs.get('r_stop') 79 if len(r_stop) != replica: 80 raise Exception('r_stop does not match number of replicas') 81 else: 82 r_stop = [None] * replica 83 84 if 'r_step' in kwargs: 85 r_step = kwargs.get('r_step') 86 else: 87 r_step = 1 88 89 print('Read reweighting factors from', prefix[:-1], ',', 90 replica, 'replica', end='') 91 92 if names is None: 93 rep_names = [] 94 for entry in ls: 95 truncated_entry = entry 96 suffixes = [".dat", ".rwms", ".ms1"] 97 for suffix in suffixes: 98 if truncated_entry.endswith(suffix): 99 truncated_entry = truncated_entry[0:-len(suffix)] 100 idx = truncated_entry.index('r') 101 rep_names.append(truncated_entry[:idx] + '|' + truncated_entry[idx:]) 102 else: 103 rep_names = names 104 105 print_err = 0 106 if 'print_err' in kwargs: 107 print_err = 1 108 print() 109 110 deltas = [] 111 112 configlist = [] 113 r_start_index = [] 114 r_stop_index = [] 115 116 for rep in range(replica): 117 tmp_array = [] 118 with open(path + '/' + ls[rep], 'rb') as fp: 119 120 t = fp.read(4) # number of reweighting factors 121 if rep == 0: 122 nrw = struct.unpack('i', t)[0] 123 if version == '2.0': 124 nrw = int(nrw / 2) 125 for k in range(nrw): 126 deltas.append([]) 127 else: 128 if ((nrw != struct.unpack('i', t)[0] and (not version == '2.0')) or (nrw != struct.unpack('i', t)[0] / 2 and version == '2.0')): 129 raise Exception('Error: different number of reweighting factors for replicum', rep) 130 131 for k in range(nrw): 132 tmp_array.append([]) 133 134 # This block is necessary for openQCD1.6 and openQCD2.0 ms1 files 135 nfct = [] 136 if version in ['1.6', '2.0']: 137 for i in range(nrw): 138 t = fp.read(4) 139 nfct.append(struct.unpack('i', t)[0]) 140 else: 141 for i in range(nrw): 142 nfct.append(1) 143 144 nsrc = [] 145 for i in range(nrw): 146 t = fp.read(4) 147 nsrc.append(struct.unpack('i', t)[0]) 148 if version == '2.0': 149 if not struct.unpack('i', fp.read(4))[0] == 0: 150 print('something is wrong!') 151 152 configlist.append([]) 153 while True: 154 t = fp.read(4) 155 if len(t) < 4: 156 break 157 config_no = struct.unpack('i', t)[0] 158 configlist[-1].append(config_no) 159 for i in range(nrw): 160 if (version == '2.0'): 161 tmpd = _read_array_openQCD2(fp) 162 tmpd = _read_array_openQCD2(fp) 163 tmp_rw = tmpd['arr'] 164 tmp_nfct = 1.0 165 for j in range(tmpd['n'][0]): 166 tmp_nfct *= np.mean(np.exp(-np.asarray(tmp_rw[j]))) 167 if print_err: 168 print(config_no, i, j, 169 np.mean(np.exp(-np.asarray(tmp_rw[j]))), 170 np.std(np.exp(-np.asarray(tmp_rw[j])))) 171 print('Sources:', 172 np.exp(-np.asarray(tmp_rw[j]))) 173 print('Partial factor:', tmp_nfct) 174 elif version == '1.6' or version == '1.4': 175 tmp_nfct = 1.0 176 for j in range(nfct[i]): 177 t = fp.read(8 * nsrc[i]) 178 t = fp.read(8 * nsrc[i]) 179 tmp_rw = struct.unpack('d' * nsrc[i], t) 180 tmp_nfct *= np.mean(np.exp(-np.asarray(tmp_rw))) 181 if print_err: 182 print(config_no, i, j, 183 np.mean(np.exp(-np.asarray(tmp_rw))), 184 np.std(np.exp(-np.asarray(tmp_rw)))) 185 print('Sources:', np.exp(-np.asarray(tmp_rw))) 186 print('Partial factor:', tmp_nfct) 187 tmp_array[i].append(tmp_nfct) 188 189 diffmeas = configlist[-1][-1] - configlist[-1][-2] 190 configlist[-1] = [item // diffmeas for item in configlist[-1]] 191 if configlist[-1][0] > 1 and diffmeas > 1: 192 warnings.warn('Assume thermalization and that the first measurement belongs to the first config.') 193 offset = configlist[-1][0] - 1 194 configlist[-1] = [item - offset for item in configlist[-1]] 195 196 if r_start[rep] is None: 197 r_start_index.append(0) 198 else: 199 try: 200 r_start_index.append(configlist[-1].index(r_start[rep])) 201 except ValueError: 202 raise Exception('Config %d not in file with range [%d, %d]' % ( 203 r_start[rep], configlist[-1][0], configlist[-1][-1])) from None 204 205 if r_stop[rep] is None: 206 r_stop_index.append(len(configlist[-1]) - 1) 207 else: 208 try: 209 r_stop_index.append(configlist[-1].index(r_stop[rep])) 210 except ValueError: 211 raise Exception('Config %d not in file with range [%d, %d]' % ( 212 r_stop[rep], configlist[-1][0], configlist[-1][-1])) from None 213 214 for k in range(nrw): 215 deltas[k].append(tmp_array[k][r_start_index[rep]:r_stop_index[rep] + 1][::r_step]) 216 217 if np.any([len(np.unique(np.diff(cl))) != 1 for cl in configlist]): 218 raise Exception('Irregular spaced data in input file!', [len(np.unique(np.diff(cl))) for cl in configlist]) 219 stepsizes = [list(np.unique(np.diff(cl)))[0] for cl in configlist] 220 if np.any([step != 1 for step in stepsizes]): 221 warnings.warn('Stepsize between configurations is greater than one!' + str(stepsizes), RuntimeWarning) 222 223 print(',', nrw, 'reweighting factors with', nsrc, 'sources') 224 result = [] 225 idl = [range(configlist[rep][r_start_index[rep]], configlist[rep][r_stop_index[rep]] + 1, r_step) for rep in range(replica)] 226 227 for t in range(nrw): 228 result.append(Obs(deltas[t], rep_names, idl=idl)) 229 return result 230 231 232def extract_t0(path, prefix, dtr_read, xmin, spatial_extent, fit_range=5, **kwargs): 233 """Extract t0 from given .ms.dat files. Returns t0 as Obs. 234 235 It is assumed that all boundary effects have 236 sufficiently decayed at x0=xmin. 237 The data around the zero crossing of t^2<E> - 0.3 238 is fitted with a linear function 239 from which the exact root is extracted. 240 241 It is assumed that one measurement is performed for each config. 242 If this is not the case, the resulting idl, as well as the handling 243 of r_start, r_stop and r_step is wrong and the user has to correct 244 this in the resulting observable. 245 246 Parameters 247 ---------- 248 path : str 249 Path to .ms.dat files 250 prefix : str 251 Ensemble prefix 252 dtr_read : int 253 Determines how many trajectories should be skipped 254 when reading the ms.dat files. 255 Corresponds to dtr_cnfg / dtr_ms in the openQCD input file. 256 xmin : int 257 First timeslice where the boundary 258 effects have sufficiently decayed. 259 spatial_extent : int 260 spatial extent of the lattice, required for normalization. 261 fit_range : int 262 Number of data points left and right of the zero 263 crossing to be included in the linear fit. (Default: 5) 264 r_start : list 265 list which contains the first config to be read for each replicum. 266 r_stop : list 267 list which contains the last config to be read for each replicum. 268 r_step : int 269 integer that defines a fixed step size between two measurements (in units of configs) 270 If not given, r_step=1 is assumed. 271 plaquette : bool 272 If true extract the plaquette estimate of t0 instead. 273 names : list 274 list of names that is assigned to the data according according 275 to the order in the file list. Use careful, if you do not provide file names! 276 files : list 277 list which contains the filenames to be read. No automatic detection of 278 files performed if given. 279 plot_fit : bool 280 If true, the fit for the extraction of t0 is shown together with the data. 281 assume_thermalization : bool 282 If True: If the first record divided by the distance between two measurements is larger than 283 1, it is assumed that this is due to thermalization and the first measurement belongs 284 to the first config (default). 285 If False: The config numbers are assumed to be traj_number // difference 286 """ 287 288 ls = [] 289 for (dirpath, dirnames, filenames) in os.walk(path): 290 ls.extend(filenames) 291 break 292 293 if not ls: 294 raise Exception('Error, directory not found') 295 296 if 'files' in kwargs: 297 ls = kwargs.get('files') 298 else: 299 for exc in ls: 300 if not fnmatch.fnmatch(exc, prefix + '*.ms.dat'): 301 ls = list(set(ls) - set([exc])) 302 if len(ls) > 1: 303 ls.sort(key=lambda x: int(re.findall(r'\d+', x[len(prefix):])[0])) 304 replica = len(ls) 305 306 if 'r_start' in kwargs: 307 r_start = kwargs.get('r_start') 308 if len(r_start) != replica: 309 raise Exception('r_start does not match number of replicas') 310 r_start = [o if o else None for o in r_start] 311 else: 312 r_start = [None] * replica 313 314 if 'r_stop' in kwargs: 315 r_stop = kwargs.get('r_stop') 316 if len(r_stop) != replica: 317 raise Exception('r_stop does not match number of replicas') 318 else: 319 r_stop = [None] * replica 320 321 if 'r_step' in kwargs: 322 r_step = kwargs.get('r_step') 323 else: 324 r_step = 1 325 326 print('Extract t0 from', prefix, ',', replica, 'replica') 327 328 if 'names' in kwargs: 329 rep_names = kwargs.get('names') 330 else: 331 rep_names = [] 332 for entry in ls: 333 truncated_entry = entry.split('.')[0] 334 idx = truncated_entry.index('r') 335 rep_names.append(truncated_entry[:idx] + '|' + truncated_entry[idx:]) 336 337 Ysum = [] 338 339 configlist = [] 340 r_start_index = [] 341 r_stop_index = [] 342 343 for rep in range(replica): 344 345 with open(path + '/' + ls[rep], 'rb') as fp: 346 t = fp.read(12) 347 header = struct.unpack('iii', t) 348 if rep == 0: 349 dn = header[0] 350 nn = header[1] 351 tmax = header[2] 352 elif dn != header[0] or nn != header[1] or tmax != header[2]: 353 raise Exception('Replica parameters do not match.') 354 355 t = fp.read(8) 356 if rep == 0: 357 eps = struct.unpack('d', t)[0] 358 print('Step size:', eps, ', Maximal t value:', dn * (nn) * eps) 359 elif eps != struct.unpack('d', t)[0]: 360 raise Exception('Values for eps do not match among replica.') 361 362 Ysl = [] 363 364 configlist.append([]) 365 while True: 366 t = fp.read(4) 367 if (len(t) < 4): 368 break 369 nc = struct.unpack('i', t)[0] 370 configlist[-1].append(nc) 371 372 t = fp.read(8 * tmax * (nn + 1)) 373 if kwargs.get('plaquette'): 374 if nc % dtr_read == 0: 375 Ysl.append(struct.unpack('d' * tmax * (nn + 1), t)) 376 t = fp.read(8 * tmax * (nn + 1)) 377 if not kwargs.get('plaquette'): 378 if nc % dtr_read == 0: 379 Ysl.append(struct.unpack('d' * tmax * (nn + 1), t)) 380 t = fp.read(8 * tmax * (nn + 1)) 381 382 Ysum.append([]) 383 for i, item in enumerate(Ysl): 384 Ysum[-1].append([np.mean(item[current + xmin: 385 current + tmax - xmin]) 386 for current in range(0, len(item), tmax)]) 387 388 diffmeas = configlist[-1][-1] - configlist[-1][-2] 389 configlist[-1] = [item // diffmeas for item in configlist[-1]] 390 if kwargs.get('assume_thermalization', True) and configlist[-1][0] > 1: 391 warnings.warn('Assume thermalization and that the first measurement belongs to the first config.') 392 offset = configlist[-1][0] - 1 393 configlist[-1] = [item - offset for item in configlist[-1]] 394 395 if r_start[rep] is None: 396 r_start_index.append(0) 397 else: 398 try: 399 r_start_index.append(configlist[-1].index(r_start[rep])) 400 except ValueError: 401 raise Exception('Config %d not in file with range [%d, %d]' % ( 402 r_start[rep], configlist[-1][0], configlist[-1][-1])) from None 403 404 if r_stop[rep] is None: 405 r_stop_index.append(len(configlist[-1]) - 1) 406 else: 407 try: 408 r_stop_index.append(configlist[-1].index(r_stop[rep])) 409 except ValueError: 410 raise Exception('Config %d not in file with range [%d, %d]' % ( 411 r_stop[rep], configlist[-1][0], configlist[-1][-1])) from None 412 413 if np.any([len(np.unique(np.diff(cl))) != 1 for cl in configlist]): 414 raise Exception('Irregular spaced data in input file!', [len(np.unique(np.diff(cl))) for cl in configlist]) 415 stepsizes = [list(np.unique(np.diff(cl)))[0] for cl in configlist] 416 if np.any([step != 1 for step in stepsizes]): 417 warnings.warn('Stepsize between configurations is greater than one!' + str(stepsizes), RuntimeWarning) 418 419 idl = [range(configlist[rep][r_start_index[rep]], configlist[rep][r_stop_index[rep]] + 1, r_step) for rep in range(replica)] 420 t2E_dict = {} 421 for n in range(nn + 1): 422 samples = [] 423 for nrep, rep in enumerate(Ysum): 424 samples.append([]) 425 for cnfg in rep: 426 samples[-1].append(cnfg[n]) 427 samples[-1] = samples[-1][r_start_index[nrep]:r_stop_index[nrep] + 1][::r_step] 428 new_obs = Obs(samples, rep_names, idl=idl) 429 t2E_dict[n * dn * eps] = (n * dn * eps) ** 2 * new_obs / (spatial_extent ** 3) - 0.3 430 431 zero_crossing = np.argmax(np.array( 432 [o.value for o in t2E_dict.values()]) > 0.0) 433 434 x = list(t2E_dict.keys())[zero_crossing - fit_range: 435 zero_crossing + fit_range] 436 y = list(t2E_dict.values())[zero_crossing - fit_range: 437 zero_crossing + fit_range] 438 [o.gamma_method() for o in y] 439 440 fit_result = fit_lin(x, y) 441 442 if kwargs.get('plot_fit'): 443 plt.figure() 444 gs = gridspec.GridSpec(2, 1, height_ratios=[3, 1], wspace=0.0, hspace=0.0) 445 ax0 = plt.subplot(gs[0]) 446 xmore = list(t2E_dict.keys())[zero_crossing - fit_range - 2: zero_crossing + fit_range + 2] 447 ymore = list(t2E_dict.values())[zero_crossing - fit_range - 2: zero_crossing + fit_range + 2] 448 [o.gamma_method() for o in ymore] 449 ax0.errorbar(xmore, [yi.value for yi in ymore], yerr=[yi.dvalue for yi in ymore], fmt='x') 450 xplot = np.linspace(np.min(x), np.max(x)) 451 yplot = [fit_result[0] + fit_result[1] * xi for xi in xplot] 452 [yi.gamma_method() for yi in yplot] 453 ax0.fill_between(xplot, y1=[yi.value - yi.dvalue for yi in yplot], y2=[yi.value + yi.dvalue for yi in yplot]) 454 retval = (-fit_result[0] / fit_result[1]) 455 retval.gamma_method() 456 ylim = ax0.get_ylim() 457 ax0.fill_betweenx(ylim, x1=retval.value - retval.dvalue, x2=retval.value + retval.dvalue, color='gray', alpha=0.4) 458 ax0.set_ylim(ylim) 459 ax0.set_ylabel(r'$t^2 \langle E(t) \rangle - 0.3 $') 460 xlim = ax0.get_xlim() 461 462 fit_res = [fit_result[0] + fit_result[1] * xi for xi in x] 463 residuals = (np.asarray([o.value for o in y]) - [o.value for o in fit_res]) / np.asarray([o.dvalue for o in y]) 464 ax1 = plt.subplot(gs[1]) 465 ax1.plot(x, residuals, 'ko', ls='none', markersize=5) 466 ax1.tick_params(direction='out') 467 ax1.tick_params(axis="x", bottom=True, top=True, labelbottom=True) 468 ax1.axhline(y=0.0, ls='--', color='k') 469 ax1.fill_between(xlim, -1.0, 1.0, alpha=0.1, facecolor='k') 470 ax1.set_xlim(xlim) 471 ax1.set_ylabel('Residuals') 472 ax1.set_xlabel(r'$t/a^2$') 473 474 plt.draw() 475 return -fit_result[0] / fit_result[1] 476 477 478def _parse_array_openQCD2(d, n, size, wa, quadrupel=False): 479 arr = [] 480 if d == 2: 481 for i in range(n[0]): 482 tmp = wa[i * n[1]:(i + 1) * n[1]] 483 if quadrupel: 484 tmp2 = [] 485 for j in range(0, len(tmp), 2): 486 tmp2.append(tmp[j]) 487 arr.append(tmp2) 488 else: 489 arr.append(np.asarray(tmp)) 490 491 else: 492 raise Exception('Only two-dimensional arrays supported!') 493 494 return arr 495 496 497def _read_array_openQCD2(fp): 498 t = fp.read(4) 499 d = struct.unpack('i', t)[0] 500 t = fp.read(4 * d) 501 n = struct.unpack('%di' % (d), t) 502 t = fp.read(4) 503 size = struct.unpack('i', t)[0] 504 if size == 4: 505 types = 'i' 506 elif size == 8: 507 types = 'd' 508 elif size == 16: 509 types = 'dd' 510 else: 511 raise Exception("Type for size '" + str(size) + "' not known.") 512 m = n[0] 513 for i in range(1, d): 514 m *= n[i] 515 516 t = fp.read(m * size) 517 tmp = struct.unpack('%d%s' % (m, types), t) 518 519 arr = _parse_array_openQCD2(d, n, size, tmp, quadrupel=True) 520 return {'d': d, 'n': n, 'size': size, 'arr': arr} 521 522 523def read_qtop(path, prefix, c, dtr_cnfg=1, version="openQCD", **kwargs): 524 """Read the topologial charge based on openQCD gradient flow measurements. 525 526 Parameters 527 ---------- 528 path : str 529 path of the measurement files 530 prefix : str 531 prefix of the measurement files, e.g. <prefix>_id0_r0.ms.dat. 532 Ignored if file names are passed explicitly via keyword files. 533 c : double 534 Smearing radius in units of the lattice extent, c = sqrt(8 t0) / L. 535 dtr_cnfg : int 536 (optional) parameter that specifies the number of measurements 537 between two configs. 538 If it is not set, the distance between two measurements 539 in the file is assumed to be the distance between two configurations. 540 steps : int 541 (optional) Distance between two configurations in units of trajectories / 542 cycles. Assumed to be the distance between two measurements * dtr_cnfg if not given 543 version : str 544 Either openQCD or sfqcd, depending on the data. 545 L : int 546 spatial length of the lattice in L/a. 547 HAS to be set if version != sfqcd, since openQCD does not provide 548 this in the header 549 r_start : list 550 list which contains the first config to be read for each replicum. 551 r_stop : list 552 list which contains the last config to be read for each replicum. 553 files : list 554 specify the exact files that need to be read 555 from path, practical if e.g. only one replicum is needed 556 postfix : str 557 postfix of the file to read, e.g. '.gfms.dat' for openQCD-files 558 names : list 559 Alternative labeling for replicas/ensembles. 560 Has to have the appropriate length. 561 Zeuthen_flow : bool 562 (optional) If True, the Zeuthen flow is used for Qtop. Only possible 563 for version=='sfqcd' If False, the Wilson flow is used. 564 integer_charge : bool 565 If True, the charge is rounded towards the nearest integer on each config. 566 """ 567 568 return _read_flow_obs(path, prefix, c, dtr_cnfg=dtr_cnfg, version=version, obspos=0, **kwargs) 569 570 571def read_gf_coupling(path, prefix, c, dtr_cnfg=1, Zeuthen_flow=True, **kwargs): 572 """Read the gradient flow coupling based on sfqcd gradient flow measurements. See 1607.06423 for details. 573 574 Note: The current implementation only works for c=0.3 and T=L. The definition of the coupling in 1607.06423 requires projection to topological charge zero which is not done within this function but has to be performed in a separate step. 575 576 Parameters 577 ---------- 578 path : str 579 path of the measurement files 580 prefix : str 581 prefix of the measurement files, e.g. <prefix>_id0_r0.ms.dat. 582 Ignored if file names are passed explicitly via keyword files. 583 c : double 584 Smearing radius in units of the lattice extent, c = sqrt(8 t0) / L. 585 dtr_cnfg : int 586 (optional) parameter that specifies the number of measurements 587 between two configs. 588 If it is not set, the distance between two measurements 589 in the file is assumed to be the distance between two configurations. 590 steps : int 591 (optional) Distance between two configurations in units of trajectories / 592 cycles. Assumed to be the distance between two measurements * dtr_cnfg if not given 593 r_start : list 594 list which contains the first config to be read for each replicum. 595 r_stop : list 596 list which contains the last config to be read for each replicum. 597 files : list 598 specify the exact files that need to be read 599 from path, practical if e.g. only one replicum is needed 600 names : list 601 Alternative labeling for replicas/ensembles. 602 Has to have the appropriate length. 603 postfix : str 604 postfix of the file to read, e.g. '.gfms.dat' for openQCD-files 605 Zeuthen_flow : bool 606 (optional) If True, the Zeuthen flow is used for the coupling. If False, the Wilson flow is used. 607 """ 608 609 if c != 0.3: 610 raise Exception("The required lattice norm is only implemented for c=0.3 at the moment.") 611 612 plaq = _read_flow_obs(path, prefix, c, dtr_cnfg=dtr_cnfg, version="sfqcd", obspos=6, sum_t=False, Zeuthen_flow=Zeuthen_flow, integer_charge=False, **kwargs) 613 C2x1 = _read_flow_obs(path, prefix, c, dtr_cnfg=dtr_cnfg, version="sfqcd", obspos=7, sum_t=False, Zeuthen_flow=Zeuthen_flow, integer_charge=False, **kwargs) 614 L = plaq.tag["L"] 615 T = plaq.tag["T"] 616 617 if T != L: 618 raise Exception("The required lattice norm is only implemented for T=L at the moment.") 619 620 if Zeuthen_flow is not True: 621 raise Exception("The required lattice norm is only implemented for the Zeuthen flow at the moment.") 622 623 t = (c * L) ** 2 / 8 624 625 normdict = {4: 0.012341170468270, 626 6: 0.010162691462430, 627 8: 0.009031614807931, 628 10: 0.008744966371393, 629 12: 0.008650917856809, 630 14: 8.611154391267955E-03, 631 16: 0.008591758449508, 632 20: 0.008575359627103, 633 24: 0.008569387847540, 634 28: 8.566803713382559E-03, 635 32: 0.008565541650006, 636 40: 8.564480684962046E-03, 637 48: 8.564098025073460E-03, 638 64: 8.563853943383087E-03} 639 640 return t * t * (5 / 3 * plaq - 1 / 12 * C2x1) / normdict[L] 641 642 643def _read_flow_obs(path, prefix, c, dtr_cnfg=1, version="openQCD", obspos=0, sum_t=True, **kwargs): 644 """Read a flow observable based on openQCD gradient flow measurements. 645 646 Parameters 647 ---------- 648 path : str 649 path of the measurement files 650 prefix : str 651 prefix of the measurement files, e.g. <prefix>_id0_r0.ms.dat. 652 Ignored if file names are passed explicitly via keyword files. 653 c : double 654 Smearing radius in units of the lattice extent, c = sqrt(8 t0) / L. 655 dtr_cnfg : int 656 (optional) parameter that specifies the number of measurements 657 between two configs. 658 If it is not set, the distance between two measurements 659 in the file is assumed to be the distance between two configurations. 660 steps : int 661 (optional) Distance between two configurations in units of trajectories / 662 cycles. Assumed to be the distance between two measurements * dtr_cnfg if not given 663 version : str 664 Either openQCD or sfqcd, depending on the data. 665 obspos : int 666 position of the obeservable in the measurement file. Only relevant for sfqcd files. 667 sum_t : bool 668 If true sum over all timeslices, if false only take the value at T/2. 669 L : int 670 spatial length of the lattice in L/a. 671 HAS to be set if version != sfqcd, since openQCD does not provide 672 this in the header 673 r_start : list 674 list which contains the first config to be read for each replicum. 675 r_stop : list 676 list which contains the last config to be read for each replicum. 677 files : list 678 specify the exact files that need to be read 679 from path, practical if e.g. only one replicum is needed 680 names : list 681 Alternative labeling for replicas/ensembles. 682 Has to have the appropriate length. 683 postfix : str 684 postfix of the file to read, e.g. '.gfms.dat' for openQCD-files 685 Zeuthen_flow : bool 686 (optional) If True, the Zeuthen flow is used for Qtop. Only possible 687 for version=='sfqcd' If False, the Wilson flow is used. 688 integer_charge : bool 689 If True, the charge is rounded towards the nearest integer on each config. 690 """ 691 known_versions = ["openQCD", "sfqcd"] 692 693 if version not in known_versions: 694 raise Exception("Unknown openQCD version.") 695 if "steps" in kwargs: 696 steps = kwargs.get("steps") 697 if version == "sfqcd": 698 if "L" in kwargs: 699 supposed_L = kwargs.get("L") 700 else: 701 supposed_L = None 702 postfix = ".gfms.dat" 703 else: 704 if "L" not in kwargs: 705 raise Exception("This version of openQCD needs you to provide the spatial length of the lattice as parameter 'L'.") 706 else: 707 L = kwargs.get("L") 708 postfix = ".ms.dat" 709 710 if "postfix" in kwargs: 711 postfix = kwargs.get("postfix") 712 713 if "files" in kwargs: 714 files = kwargs.get("files") 715 postfix = '' 716 else: 717 found = [] 718 files = [] 719 for (dirpath, dirnames, filenames) in os.walk(path + "/"): 720 found.extend(filenames) 721 break 722 for f in found: 723 if fnmatch.fnmatch(f, prefix + "*" + postfix): 724 files.append(f) 725 726 files = sorted(files) 727 728 if 'r_start' in kwargs: 729 r_start = kwargs.get('r_start') 730 if len(r_start) != len(files): 731 raise Exception('r_start does not match number of replicas') 732 r_start = [o if o else None for o in r_start] 733 else: 734 r_start = [None] * len(files) 735 736 if 'r_stop' in kwargs: 737 r_stop = kwargs.get('r_stop') 738 if len(r_stop) != len(files): 739 raise Exception('r_stop does not match number of replicas') 740 else: 741 r_stop = [None] * len(files) 742 rep_names = [] 743 744 zeuthen = kwargs.get('Zeuthen_flow', False) 745 if zeuthen and version not in ['sfqcd']: 746 raise Exception('Zeuthen flow can only be used for version==sfqcd') 747 748 r_start_index = [] 749 r_stop_index = [] 750 deltas = [] 751 configlist = [] 752 if not zeuthen: 753 obspos += 8 754 for rep, file in enumerate(files): 755 with open(path + "/" + file, "rb") as fp: 756 757 Q = [] 758 traj_list = [] 759 if version in ['sfqcd']: 760 t = fp.read(12) 761 header = struct.unpack('<iii', t) 762 zthfl = header[0] # Zeuthen flow -> if it's equal to 2 it means that the Zeuthen flow is also 'measured' (apart from the Wilson flow) 763 ncs = header[1] # number of different values for c in t_flow=1/8 c² L² -> measurements done for ncs c's 764 tmax = header[2] # lattice T/a 765 766 t = fp.read(12) 767 Ls = struct.unpack('<iii', t) 768 if (Ls[0] == Ls[1] and Ls[1] == Ls[2]): 769 L = Ls[0] 770 if not (supposed_L == L) and supposed_L: 771 raise Exception("It seems the length given in the header and by you contradict each other") 772 else: 773 raise Exception("Found more than one spatial length in header!") 774 775 t = fp.read(16) 776 header2 = struct.unpack('<dd', t) 777 tol = header2[0] 778 cmax = header2[1] # highest value of c used 779 780 if c > cmax: 781 raise Exception('Flow has been determined between c=0 and c=%lf with tolerance %lf' % (cmax, tol)) 782 783 if (zthfl == 2): 784 nfl = 2 # number of flows 785 else: 786 nfl = 1 787 iobs = 8 * nfl # number of flow observables calculated 788 789 while True: 790 t = fp.read(4) 791 if (len(t) < 4): 792 break 793 traj_list.append(struct.unpack('i', t)[0]) # trajectory number when measurement was done 794 795 for j in range(ncs + 1): 796 for i in range(iobs): 797 t = fp.read(8 * tmax) 798 if (i == obspos): # determines the flow observable -> i=0 <-> Zeuthen flow 799 Q.append(struct.unpack('d' * tmax, t)) 800 801 else: 802 t = fp.read(12) 803 header = struct.unpack('<iii', t) 804 # step size in integration steps "dnms" 805 dn = header[0] 806 # number of measurements, so "ntot"/dn 807 nn = header[1] 808 # lattice T/a 809 tmax = header[2] 810 811 t = fp.read(8) 812 eps = struct.unpack('d', t)[0] 813 814 while True: 815 t = fp.read(4) 816 if (len(t) < 4): 817 break 818 traj_list.append(struct.unpack('i', t)[0]) 819 # Wsl 820 t = fp.read(8 * tmax * (nn + 1)) 821 # Ysl 822 t = fp.read(8 * tmax * (nn + 1)) 823 # Qsl, which is asked for in this method 824 t = fp.read(8 * tmax * (nn + 1)) 825 # unpack the array of Qtops, 826 # on each timeslice t=0,...,tmax-1 and the 827 # measurement number in = 0...nn (see README.qcd1) 828 tmpd = struct.unpack('d' * tmax * (nn + 1), t) 829 Q.append(tmpd) 830 831 if len(np.unique(np.diff(traj_list))) != 1: 832 raise Exception("Irregularities in stepsize found") 833 else: 834 if 'steps' in kwargs: 835 if steps != traj_list[1] - traj_list[0]: 836 raise Exception("steps and the found stepsize are not the same") 837 else: 838 steps = traj_list[1] - traj_list[0] 839 840 configlist.append([tr // steps // dtr_cnfg for tr in traj_list]) 841 if configlist[-1][0] > 1: 842 offset = configlist[-1][0] - 1 843 warnings.warn('Assume thermalization and that the first measurement belongs to the first config. Offset = %d configs (%d trajectories / cycles)' % ( 844 offset, offset * steps)) 845 configlist[-1] = [item - offset for item in configlist[-1]] 846 847 if r_start[rep] is None: 848 r_start_index.append(0) 849 else: 850 try: 851 r_start_index.append(configlist[-1].index(r_start[rep])) 852 except ValueError: 853 raise Exception('Config %d not in file with range [%d, %d]' % ( 854 r_start[rep], configlist[-1][0], configlist[-1][-1])) from None 855 856 if r_stop[rep] is None: 857 r_stop_index.append(len(configlist[-1]) - 1) 858 else: 859 try: 860 r_stop_index.append(configlist[-1].index(r_stop[rep])) 861 except ValueError: 862 raise Exception('Config %d not in file with range [%d, %d]' % ( 863 r_stop[rep], configlist[-1][0], configlist[-1][-1])) from None 864 865 if version in ['sfqcd']: 866 cstepsize = cmax / ncs 867 index_aim = round(c / cstepsize) 868 else: 869 t_aim = (c * L) ** 2 / 8 870 index_aim = round(t_aim / eps / dn) 871 872 Q_sum = [] 873 for i, item in enumerate(Q): 874 if sum_t is True: 875 Q_sum.append([sum(item[current:current + tmax]) 876 for current in range(0, len(item), tmax)]) 877 else: 878 Q_sum.append([item[int(tmax / 2)]]) 879 Q_top = [] 880 if version in ['sfqcd']: 881 for i in range(len(Q_sum) // (ncs + 1)): 882 Q_top.append(Q_sum[i * (ncs + 1) + index_aim][0]) 883 else: 884 for i in range(len(Q) // dtr_cnfg): 885 Q_top.append(Q_sum[dtr_cnfg * i][index_aim]) 886 if len(Q_top) != len(traj_list) // dtr_cnfg: 887 raise Exception("qtops and traj_list dont have the same length") 888 889 if kwargs.get('integer_charge', False): 890 Q_top = [round(q) for q in Q_top] 891 892 truncated_file = file[:-len(postfix)] 893 894 if "names" not in kwargs: 895 try: 896 idx = truncated_file.index('r') 897 except Exception: 898 if "names" not in kwargs: 899 raise Exception("Automatic recognition of replicum failed, please enter the key word 'names'.") 900 ens_name = truncated_file[:idx] 901 rep_names.append(ens_name + '|' + truncated_file[idx:]) 902 else: 903 names = kwargs.get("names") 904 rep_names = names 905 deltas.append(Q_top) 906 907 idl = [range(int(configlist[rep][r_start_index[rep]]), int(configlist[rep][r_stop_index[rep]]) + 1, 1) for rep in range(len(deltas))] 908 deltas = [deltas[nrep][r_start_index[nrep]:r_stop_index[nrep] + 1] for nrep in range(len(deltas))] 909 result = Obs(deltas, rep_names, idl=idl) 910 result.tag = {"T": tmax - 1, 911 "L": L} 912 return result 913 914 915def qtop_projection(qtop, target=0): 916 """Returns the projection to the topological charge sector defined by target. 917 918 Parameters 919 ---------- 920 path : Obs 921 Topological charge. 922 target : int 923 Specifies the topological sector to be reweighted to (default 0) 924 """ 925 if qtop.reweighted: 926 raise Exception('You can not use a reweighted observable for reweighting!') 927 928 proj_qtop = [] 929 for n in qtop.deltas: 930 proj_qtop.append(np.array([1 if round(qtop.value + q) == target else 0 for q in qtop.deltas[n]])) 931 932 reto = Obs(proj_qtop, qtop.names, idl=[qtop.idl[name] for name in qtop.names]) 933 reto.is_merged = qtop.is_merged 934 return reto 935 936 937def read_qtop_sector(path, prefix, c, target=0, **kwargs): 938 """Constructs reweighting factors to a specified topological sector. 939 940 Parameters 941 ---------- 942 path : str 943 path of the measurement files 944 prefix : str 945 prefix of the measurement files, e.g. <prefix>_id0_r0.ms.dat 946 c : double 947 Smearing radius in units of the lattice extent, c = sqrt(8 t0) / L 948 target : int 949 Specifies the topological sector to be reweighted to (default 0) 950 dtr_cnfg : int 951 (optional) parameter that specifies the number of trajectories 952 between two configs. 953 if it is not set, the distance between two measurements 954 in the file is assumed to be the distance between two configurations. 955 steps : int 956 (optional) Distance between two configurations in units of trajectories / 957 cycles. Assumed to be the distance between two measurements * dtr_cnfg if not given 958 version : str 959 version string of the openQCD (sfqcd) version used to create 960 the ensemble. Default is 2.0. May also be set to sfqcd. 961 L : int 962 spatial length of the lattice in L/a. 963 HAS to be set if version != sfqcd, since openQCD does not provide 964 this in the header 965 r_start : list 966 offset of the first ensemble, making it easier to match 967 later on with other Obs 968 r_stop : list 969 last configurations that need to be read (per replicum) 970 files : list 971 specify the exact files that need to be read 972 from path, practical if e.g. only one replicum is needed 973 names : list 974 Alternative labeling for replicas/ensembles. 975 Has to have the appropriate length 976 Zeuthen_flow : bool 977 (optional) If True, the Zeuthen flow is used for Qtop. Only possible 978 for version=='sfqcd' If False, the Wilson flow is used. 979 """ 980 981 if not isinstance(target, int): 982 raise Exception("'target' has to be an integer.") 983 984 kwargs['integer_charge'] = True 985 qtop = read_qtop(path, prefix, c, **kwargs) 986 987 return qtop_projection(qtop, target=target)
14def read_rwms(path, prefix, version='2.0', names=None, **kwargs): 15 """Read rwms format from given folder structure. Returns a list of length nrw 16 17 Parameters 18 ---------- 19 path : str 20 path that contains the data files 21 prefix : str 22 all files in path that start with prefix are considered as input files. 23 May be used together postfix to consider only special file endings. 24 Prefix is ignored, if the keyword 'files' is used. 25 version : str 26 version of openQCD, default 2.0 27 names : list 28 list of names that is assigned to the data according according 29 to the order in the file list. Use careful, if you do not provide file names! 30 r_start : list 31 list which contains the first config to be read for each replicum 32 r_stop : list 33 list which contains the last config to be read for each replicum 34 r_step : int 35 integer that defines a fixed step size between two measurements (in units of configs) 36 If not given, r_step=1 is assumed. 37 postfix : str 38 postfix of the file to read, e.g. '.ms1' for openQCD-files 39 files : list 40 list which contains the filenames to be read. No automatic detection of 41 files performed if given. 42 print_err : bool 43 Print additional information that is useful for debugging. 44 """ 45 known_oqcd_versions = ['1.4', '1.6', '2.0'] 46 if not (version in known_oqcd_versions): 47 raise Exception('Unknown openQCD version defined!') 48 print("Working with openQCD version " + version) 49 if 'postfix' in kwargs: 50 postfix = kwargs.get('postfix') 51 else: 52 postfix = '' 53 ls = [] 54 for (dirpath, dirnames, filenames) in os.walk(path): 55 ls.extend(filenames) 56 break 57 58 if not ls: 59 raise Exception(f"Error, directory '{path}' not found") 60 if 'files' in kwargs: 61 ls = kwargs.get('files') 62 else: 63 for exc in ls: 64 if not fnmatch.fnmatch(exc, prefix + '*' + postfix + '.dat'): 65 ls = list(set(ls) - set([exc])) 66 if len(ls) > 1: 67 ls.sort(key=lambda x: int(re.findall(r'\d+', x[len(prefix):])[0])) 68 replica = len(ls) 69 70 if 'r_start' in kwargs: 71 r_start = kwargs.get('r_start') 72 if len(r_start) != replica: 73 raise Exception('r_start does not match number of replicas') 74 r_start = [o if o else None for o in r_start] 75 else: 76 r_start = [None] * replica 77 78 if 'r_stop' in kwargs: 79 r_stop = kwargs.get('r_stop') 80 if len(r_stop) != replica: 81 raise Exception('r_stop does not match number of replicas') 82 else: 83 r_stop = [None] * replica 84 85 if 'r_step' in kwargs: 86 r_step = kwargs.get('r_step') 87 else: 88 r_step = 1 89 90 print('Read reweighting factors from', prefix[:-1], ',', 91 replica, 'replica', end='') 92 93 if names is None: 94 rep_names = [] 95 for entry in ls: 96 truncated_entry = entry 97 suffixes = [".dat", ".rwms", ".ms1"] 98 for suffix in suffixes: 99 if truncated_entry.endswith(suffix): 100 truncated_entry = truncated_entry[0:-len(suffix)] 101 idx = truncated_entry.index('r') 102 rep_names.append(truncated_entry[:idx] + '|' + truncated_entry[idx:]) 103 else: 104 rep_names = names 105 106 print_err = 0 107 if 'print_err' in kwargs: 108 print_err = 1 109 print() 110 111 deltas = [] 112 113 configlist = [] 114 r_start_index = [] 115 r_stop_index = [] 116 117 for rep in range(replica): 118 tmp_array = [] 119 with open(path + '/' + ls[rep], 'rb') as fp: 120 121 t = fp.read(4) # number of reweighting factors 122 if rep == 0: 123 nrw = struct.unpack('i', t)[0] 124 if version == '2.0': 125 nrw = int(nrw / 2) 126 for k in range(nrw): 127 deltas.append([]) 128 else: 129 if ((nrw != struct.unpack('i', t)[0] and (not version == '2.0')) or (nrw != struct.unpack('i', t)[0] / 2 and version == '2.0')): 130 raise Exception('Error: different number of reweighting factors for replicum', rep) 131 132 for k in range(nrw): 133 tmp_array.append([]) 134 135 # This block is necessary for openQCD1.6 and openQCD2.0 ms1 files 136 nfct = [] 137 if version in ['1.6', '2.0']: 138 for i in range(nrw): 139 t = fp.read(4) 140 nfct.append(struct.unpack('i', t)[0]) 141 else: 142 for i in range(nrw): 143 nfct.append(1) 144 145 nsrc = [] 146 for i in range(nrw): 147 t = fp.read(4) 148 nsrc.append(struct.unpack('i', t)[0]) 149 if version == '2.0': 150 if not struct.unpack('i', fp.read(4))[0] == 0: 151 print('something is wrong!') 152 153 configlist.append([]) 154 while True: 155 t = fp.read(4) 156 if len(t) < 4: 157 break 158 config_no = struct.unpack('i', t)[0] 159 configlist[-1].append(config_no) 160 for i in range(nrw): 161 if (version == '2.0'): 162 tmpd = _read_array_openQCD2(fp) 163 tmpd = _read_array_openQCD2(fp) 164 tmp_rw = tmpd['arr'] 165 tmp_nfct = 1.0 166 for j in range(tmpd['n'][0]): 167 tmp_nfct *= np.mean(np.exp(-np.asarray(tmp_rw[j]))) 168 if print_err: 169 print(config_no, i, j, 170 np.mean(np.exp(-np.asarray(tmp_rw[j]))), 171 np.std(np.exp(-np.asarray(tmp_rw[j])))) 172 print('Sources:', 173 np.exp(-np.asarray(tmp_rw[j]))) 174 print('Partial factor:', tmp_nfct) 175 elif version == '1.6' or version == '1.4': 176 tmp_nfct = 1.0 177 for j in range(nfct[i]): 178 t = fp.read(8 * nsrc[i]) 179 t = fp.read(8 * nsrc[i]) 180 tmp_rw = struct.unpack('d' * nsrc[i], t) 181 tmp_nfct *= np.mean(np.exp(-np.asarray(tmp_rw))) 182 if print_err: 183 print(config_no, i, j, 184 np.mean(np.exp(-np.asarray(tmp_rw))), 185 np.std(np.exp(-np.asarray(tmp_rw)))) 186 print('Sources:', np.exp(-np.asarray(tmp_rw))) 187 print('Partial factor:', tmp_nfct) 188 tmp_array[i].append(tmp_nfct) 189 190 diffmeas = configlist[-1][-1] - configlist[-1][-2] 191 configlist[-1] = [item // diffmeas for item in configlist[-1]] 192 if configlist[-1][0] > 1 and diffmeas > 1: 193 warnings.warn('Assume thermalization and that the first measurement belongs to the first config.') 194 offset = configlist[-1][0] - 1 195 configlist[-1] = [item - offset for item in configlist[-1]] 196 197 if r_start[rep] is None: 198 r_start_index.append(0) 199 else: 200 try: 201 r_start_index.append(configlist[-1].index(r_start[rep])) 202 except ValueError: 203 raise Exception('Config %d not in file with range [%d, %d]' % ( 204 r_start[rep], configlist[-1][0], configlist[-1][-1])) from None 205 206 if r_stop[rep] is None: 207 r_stop_index.append(len(configlist[-1]) - 1) 208 else: 209 try: 210 r_stop_index.append(configlist[-1].index(r_stop[rep])) 211 except ValueError: 212 raise Exception('Config %d not in file with range [%d, %d]' % ( 213 r_stop[rep], configlist[-1][0], configlist[-1][-1])) from None 214 215 for k in range(nrw): 216 deltas[k].append(tmp_array[k][r_start_index[rep]:r_stop_index[rep] + 1][::r_step]) 217 218 if np.any([len(np.unique(np.diff(cl))) != 1 for cl in configlist]): 219 raise Exception('Irregular spaced data in input file!', [len(np.unique(np.diff(cl))) for cl in configlist]) 220 stepsizes = [list(np.unique(np.diff(cl)))[0] for cl in configlist] 221 if np.any([step != 1 for step in stepsizes]): 222 warnings.warn('Stepsize between configurations is greater than one!' + str(stepsizes), RuntimeWarning) 223 224 print(',', nrw, 'reweighting factors with', nsrc, 'sources') 225 result = [] 226 idl = [range(configlist[rep][r_start_index[rep]], configlist[rep][r_stop_index[rep]] + 1, r_step) for rep in range(replica)] 227 228 for t in range(nrw): 229 result.append(Obs(deltas[t], rep_names, idl=idl)) 230 return result
Read rwms format from given folder structure. Returns a list of length nrw
Parameters
- path (str): path that contains the data files
- prefix (str): all files in path that start with prefix are considered as input files. May be used together postfix to consider only special file endings. Prefix is ignored, if the keyword 'files' is used.
- version (str): version of openQCD, default 2.0
- names (list): list of names that is assigned to the data according according to the order in the file list. Use careful, if you do not provide file names!
- r_start (list): list which contains the first config to be read for each replicum
- r_stop (list): list which contains the last config to be read for each replicum
- r_step (int): integer that defines a fixed step size between two measurements (in units of configs) If not given, r_step=1 is assumed.
- postfix (str): postfix of the file to read, e.g. '.ms1' for openQCD-files
- files (list): list which contains the filenames to be read. No automatic detection of files performed if given.
- print_err (bool): Print additional information that is useful for debugging.
233def extract_t0(path, prefix, dtr_read, xmin, spatial_extent, fit_range=5, **kwargs): 234 """Extract t0 from given .ms.dat files. Returns t0 as Obs. 235 236 It is assumed that all boundary effects have 237 sufficiently decayed at x0=xmin. 238 The data around the zero crossing of t^2<E> - 0.3 239 is fitted with a linear function 240 from which the exact root is extracted. 241 242 It is assumed that one measurement is performed for each config. 243 If this is not the case, the resulting idl, as well as the handling 244 of r_start, r_stop and r_step is wrong and the user has to correct 245 this in the resulting observable. 246 247 Parameters 248 ---------- 249 path : str 250 Path to .ms.dat files 251 prefix : str 252 Ensemble prefix 253 dtr_read : int 254 Determines how many trajectories should be skipped 255 when reading the ms.dat files. 256 Corresponds to dtr_cnfg / dtr_ms in the openQCD input file. 257 xmin : int 258 First timeslice where the boundary 259 effects have sufficiently decayed. 260 spatial_extent : int 261 spatial extent of the lattice, required for normalization. 262 fit_range : int 263 Number of data points left and right of the zero 264 crossing to be included in the linear fit. (Default: 5) 265 r_start : list 266 list which contains the first config to be read for each replicum. 267 r_stop : list 268 list which contains the last config to be read for each replicum. 269 r_step : int 270 integer that defines a fixed step size between two measurements (in units of configs) 271 If not given, r_step=1 is assumed. 272 plaquette : bool 273 If true extract the plaquette estimate of t0 instead. 274 names : list 275 list of names that is assigned to the data according according 276 to the order in the file list. Use careful, if you do not provide file names! 277 files : list 278 list which contains the filenames to be read. No automatic detection of 279 files performed if given. 280 plot_fit : bool 281 If true, the fit for the extraction of t0 is shown together with the data. 282 assume_thermalization : bool 283 If True: If the first record divided by the distance between two measurements is larger than 284 1, it is assumed that this is due to thermalization and the first measurement belongs 285 to the first config (default). 286 If False: The config numbers are assumed to be traj_number // difference 287 """ 288 289 ls = [] 290 for (dirpath, dirnames, filenames) in os.walk(path): 291 ls.extend(filenames) 292 break 293 294 if not ls: 295 raise Exception('Error, directory not found') 296 297 if 'files' in kwargs: 298 ls = kwargs.get('files') 299 else: 300 for exc in ls: 301 if not fnmatch.fnmatch(exc, prefix + '*.ms.dat'): 302 ls = list(set(ls) - set([exc])) 303 if len(ls) > 1: 304 ls.sort(key=lambda x: int(re.findall(r'\d+', x[len(prefix):])[0])) 305 replica = len(ls) 306 307 if 'r_start' in kwargs: 308 r_start = kwargs.get('r_start') 309 if len(r_start) != replica: 310 raise Exception('r_start does not match number of replicas') 311 r_start = [o if o else None for o in r_start] 312 else: 313 r_start = [None] * replica 314 315 if 'r_stop' in kwargs: 316 r_stop = kwargs.get('r_stop') 317 if len(r_stop) != replica: 318 raise Exception('r_stop does not match number of replicas') 319 else: 320 r_stop = [None] * replica 321 322 if 'r_step' in kwargs: 323 r_step = kwargs.get('r_step') 324 else: 325 r_step = 1 326 327 print('Extract t0 from', prefix, ',', replica, 'replica') 328 329 if 'names' in kwargs: 330 rep_names = kwargs.get('names') 331 else: 332 rep_names = [] 333 for entry in ls: 334 truncated_entry = entry.split('.')[0] 335 idx = truncated_entry.index('r') 336 rep_names.append(truncated_entry[:idx] + '|' + truncated_entry[idx:]) 337 338 Ysum = [] 339 340 configlist = [] 341 r_start_index = [] 342 r_stop_index = [] 343 344 for rep in range(replica): 345 346 with open(path + '/' + ls[rep], 'rb') as fp: 347 t = fp.read(12) 348 header = struct.unpack('iii', t) 349 if rep == 0: 350 dn = header[0] 351 nn = header[1] 352 tmax = header[2] 353 elif dn != header[0] or nn != header[1] or tmax != header[2]: 354 raise Exception('Replica parameters do not match.') 355 356 t = fp.read(8) 357 if rep == 0: 358 eps = struct.unpack('d', t)[0] 359 print('Step size:', eps, ', Maximal t value:', dn * (nn) * eps) 360 elif eps != struct.unpack('d', t)[0]: 361 raise Exception('Values for eps do not match among replica.') 362 363 Ysl = [] 364 365 configlist.append([]) 366 while True: 367 t = fp.read(4) 368 if (len(t) < 4): 369 break 370 nc = struct.unpack('i', t)[0] 371 configlist[-1].append(nc) 372 373 t = fp.read(8 * tmax * (nn + 1)) 374 if kwargs.get('plaquette'): 375 if nc % dtr_read == 0: 376 Ysl.append(struct.unpack('d' * tmax * (nn + 1), t)) 377 t = fp.read(8 * tmax * (nn + 1)) 378 if not kwargs.get('plaquette'): 379 if nc % dtr_read == 0: 380 Ysl.append(struct.unpack('d' * tmax * (nn + 1), t)) 381 t = fp.read(8 * tmax * (nn + 1)) 382 383 Ysum.append([]) 384 for i, item in enumerate(Ysl): 385 Ysum[-1].append([np.mean(item[current + xmin: 386 current + tmax - xmin]) 387 for current in range(0, len(item), tmax)]) 388 389 diffmeas = configlist[-1][-1] - configlist[-1][-2] 390 configlist[-1] = [item // diffmeas for item in configlist[-1]] 391 if kwargs.get('assume_thermalization', True) and configlist[-1][0] > 1: 392 warnings.warn('Assume thermalization and that the first measurement belongs to the first config.') 393 offset = configlist[-1][0] - 1 394 configlist[-1] = [item - offset for item in configlist[-1]] 395 396 if r_start[rep] is None: 397 r_start_index.append(0) 398 else: 399 try: 400 r_start_index.append(configlist[-1].index(r_start[rep])) 401 except ValueError: 402 raise Exception('Config %d not in file with range [%d, %d]' % ( 403 r_start[rep], configlist[-1][0], configlist[-1][-1])) from None 404 405 if r_stop[rep] is None: 406 r_stop_index.append(len(configlist[-1]) - 1) 407 else: 408 try: 409 r_stop_index.append(configlist[-1].index(r_stop[rep])) 410 except ValueError: 411 raise Exception('Config %d not in file with range [%d, %d]' % ( 412 r_stop[rep], configlist[-1][0], configlist[-1][-1])) from None 413 414 if np.any([len(np.unique(np.diff(cl))) != 1 for cl in configlist]): 415 raise Exception('Irregular spaced data in input file!', [len(np.unique(np.diff(cl))) for cl in configlist]) 416 stepsizes = [list(np.unique(np.diff(cl)))[0] for cl in configlist] 417 if np.any([step != 1 for step in stepsizes]): 418 warnings.warn('Stepsize between configurations is greater than one!' + str(stepsizes), RuntimeWarning) 419 420 idl = [range(configlist[rep][r_start_index[rep]], configlist[rep][r_stop_index[rep]] + 1, r_step) for rep in range(replica)] 421 t2E_dict = {} 422 for n in range(nn + 1): 423 samples = [] 424 for nrep, rep in enumerate(Ysum): 425 samples.append([]) 426 for cnfg in rep: 427 samples[-1].append(cnfg[n]) 428 samples[-1] = samples[-1][r_start_index[nrep]:r_stop_index[nrep] + 1][::r_step] 429 new_obs = Obs(samples, rep_names, idl=idl) 430 t2E_dict[n * dn * eps] = (n * dn * eps) ** 2 * new_obs / (spatial_extent ** 3) - 0.3 431 432 zero_crossing = np.argmax(np.array( 433 [o.value for o in t2E_dict.values()]) > 0.0) 434 435 x = list(t2E_dict.keys())[zero_crossing - fit_range: 436 zero_crossing + fit_range] 437 y = list(t2E_dict.values())[zero_crossing - fit_range: 438 zero_crossing + fit_range] 439 [o.gamma_method() for o in y] 440 441 fit_result = fit_lin(x, y) 442 443 if kwargs.get('plot_fit'): 444 plt.figure() 445 gs = gridspec.GridSpec(2, 1, height_ratios=[3, 1], wspace=0.0, hspace=0.0) 446 ax0 = plt.subplot(gs[0]) 447 xmore = list(t2E_dict.keys())[zero_crossing - fit_range - 2: zero_crossing + fit_range + 2] 448 ymore = list(t2E_dict.values())[zero_crossing - fit_range - 2: zero_crossing + fit_range + 2] 449 [o.gamma_method() for o in ymore] 450 ax0.errorbar(xmore, [yi.value for yi in ymore], yerr=[yi.dvalue for yi in ymore], fmt='x') 451 xplot = np.linspace(np.min(x), np.max(x)) 452 yplot = [fit_result[0] + fit_result[1] * xi for xi in xplot] 453 [yi.gamma_method() for yi in yplot] 454 ax0.fill_between(xplot, y1=[yi.value - yi.dvalue for yi in yplot], y2=[yi.value + yi.dvalue for yi in yplot]) 455 retval = (-fit_result[0] / fit_result[1]) 456 retval.gamma_method() 457 ylim = ax0.get_ylim() 458 ax0.fill_betweenx(ylim, x1=retval.value - retval.dvalue, x2=retval.value + retval.dvalue, color='gray', alpha=0.4) 459 ax0.set_ylim(ylim) 460 ax0.set_ylabel(r'$t^2 \langle E(t) \rangle - 0.3 $') 461 xlim = ax0.get_xlim() 462 463 fit_res = [fit_result[0] + fit_result[1] * xi for xi in x] 464 residuals = (np.asarray([o.value for o in y]) - [o.value for o in fit_res]) / np.asarray([o.dvalue for o in y]) 465 ax1 = plt.subplot(gs[1]) 466 ax1.plot(x, residuals, 'ko', ls='none', markersize=5) 467 ax1.tick_params(direction='out') 468 ax1.tick_params(axis="x", bottom=True, top=True, labelbottom=True) 469 ax1.axhline(y=0.0, ls='--', color='k') 470 ax1.fill_between(xlim, -1.0, 1.0, alpha=0.1, facecolor='k') 471 ax1.set_xlim(xlim) 472 ax1.set_ylabel('Residuals') 473 ax1.set_xlabel(r'$t/a^2$') 474 475 plt.draw() 476 return -fit_result[0] / fit_result[1]
Extract t0 from given .ms.dat files. Returns t0 as Obs.
It is assumed that all boundary effects have
sufficiently decayed at x0=xmin.
The data around the zero crossing of t^2
It is assumed that one measurement is performed for each config. If this is not the case, the resulting idl, as well as the handling of r_start, r_stop and r_step is wrong and the user has to correct this in the resulting observable.
Parameters
- path (str): Path to .ms.dat files
- prefix (str): Ensemble prefix
- dtr_read (int): Determines how many trajectories should be skipped when reading the ms.dat files. Corresponds to dtr_cnfg / dtr_ms in the openQCD input file.
- xmin (int): First timeslice where the boundary effects have sufficiently decayed.
- spatial_extent (int): spatial extent of the lattice, required for normalization.
- fit_range (int): Number of data points left and right of the zero crossing to be included in the linear fit. (Default: 5)
- r_start (list): list which contains the first config to be read for each replicum.
- r_stop (list): list which contains the last config to be read for each replicum.
- r_step (int): integer that defines a fixed step size between two measurements (in units of configs) If not given, r_step=1 is assumed.
- plaquette (bool): If true extract the plaquette estimate of t0 instead.
- names (list): list of names that is assigned to the data according according to the order in the file list. Use careful, if you do not provide file names!
- files (list): list which contains the filenames to be read. No automatic detection of files performed if given.
- plot_fit (bool): If true, the fit for the extraction of t0 is shown together with the data.
- assume_thermalization (bool): If True: If the first record divided by the distance between two measurements is larger than 1, it is assumed that this is due to thermalization and the first measurement belongs to the first config (default). If False: The config numbers are assumed to be traj_number // difference
524def read_qtop(path, prefix, c, dtr_cnfg=1, version="openQCD", **kwargs): 525 """Read the topologial charge based on openQCD gradient flow measurements. 526 527 Parameters 528 ---------- 529 path : str 530 path of the measurement files 531 prefix : str 532 prefix of the measurement files, e.g. <prefix>_id0_r0.ms.dat. 533 Ignored if file names are passed explicitly via keyword files. 534 c : double 535 Smearing radius in units of the lattice extent, c = sqrt(8 t0) / L. 536 dtr_cnfg : int 537 (optional) parameter that specifies the number of measurements 538 between two configs. 539 If it is not set, the distance between two measurements 540 in the file is assumed to be the distance between two configurations. 541 steps : int 542 (optional) Distance between two configurations in units of trajectories / 543 cycles. Assumed to be the distance between two measurements * dtr_cnfg if not given 544 version : str 545 Either openQCD or sfqcd, depending on the data. 546 L : int 547 spatial length of the lattice in L/a. 548 HAS to be set if version != sfqcd, since openQCD does not provide 549 this in the header 550 r_start : list 551 list which contains the first config to be read for each replicum. 552 r_stop : list 553 list which contains the last config to be read for each replicum. 554 files : list 555 specify the exact files that need to be read 556 from path, practical if e.g. only one replicum is needed 557 postfix : str 558 postfix of the file to read, e.g. '.gfms.dat' for openQCD-files 559 names : list 560 Alternative labeling for replicas/ensembles. 561 Has to have the appropriate length. 562 Zeuthen_flow : bool 563 (optional) If True, the Zeuthen flow is used for Qtop. Only possible 564 for version=='sfqcd' If False, the Wilson flow is used. 565 integer_charge : bool 566 If True, the charge is rounded towards the nearest integer on each config. 567 """ 568 569 return _read_flow_obs(path, prefix, c, dtr_cnfg=dtr_cnfg, version=version, obspos=0, **kwargs)
Read the topologial charge based on openQCD gradient flow measurements.
Parameters
- path (str): path of the measurement files
- prefix (str):
prefix of the measurement files, e.g.
_id0_r0.ms.dat. Ignored if file names are passed explicitly via keyword files. - c (double): Smearing radius in units of the lattice extent, c = sqrt(8 t0) / L.
- dtr_cnfg (int): (optional) parameter that specifies the number of measurements between two configs. If it is not set, the distance between two measurements in the file is assumed to be the distance between two configurations.
- steps (int): (optional) Distance between two configurations in units of trajectories / cycles. Assumed to be the distance between two measurements * dtr_cnfg if not given
- version (str): Either openQCD or sfqcd, depending on the data.
- L (int): spatial length of the lattice in L/a. HAS to be set if version != sfqcd, since openQCD does not provide this in the header
- r_start (list): list which contains the first config to be read for each replicum.
- r_stop (list): list which contains the last config to be read for each replicum.
- files (list): specify the exact files that need to be read from path, practical if e.g. only one replicum is needed
- postfix (str): postfix of the file to read, e.g. '.gfms.dat' for openQCD-files
- names (list): Alternative labeling for replicas/ensembles. Has to have the appropriate length.
- Zeuthen_flow (bool): (optional) If True, the Zeuthen flow is used for Qtop. Only possible for version=='sfqcd' If False, the Wilson flow is used.
- integer_charge (bool): If True, the charge is rounded towards the nearest integer on each config.
572def read_gf_coupling(path, prefix, c, dtr_cnfg=1, Zeuthen_flow=True, **kwargs): 573 """Read the gradient flow coupling based on sfqcd gradient flow measurements. See 1607.06423 for details. 574 575 Note: The current implementation only works for c=0.3 and T=L. The definition of the coupling in 1607.06423 requires projection to topological charge zero which is not done within this function but has to be performed in a separate step. 576 577 Parameters 578 ---------- 579 path : str 580 path of the measurement files 581 prefix : str 582 prefix of the measurement files, e.g. <prefix>_id0_r0.ms.dat. 583 Ignored if file names are passed explicitly via keyword files. 584 c : double 585 Smearing radius in units of the lattice extent, c = sqrt(8 t0) / L. 586 dtr_cnfg : int 587 (optional) parameter that specifies the number of measurements 588 between two configs. 589 If it is not set, the distance between two measurements 590 in the file is assumed to be the distance between two configurations. 591 steps : int 592 (optional) Distance between two configurations in units of trajectories / 593 cycles. Assumed to be the distance between two measurements * dtr_cnfg if not given 594 r_start : list 595 list which contains the first config to be read for each replicum. 596 r_stop : list 597 list which contains the last config to be read for each replicum. 598 files : list 599 specify the exact files that need to be read 600 from path, practical if e.g. only one replicum is needed 601 names : list 602 Alternative labeling for replicas/ensembles. 603 Has to have the appropriate length. 604 postfix : str 605 postfix of the file to read, e.g. '.gfms.dat' for openQCD-files 606 Zeuthen_flow : bool 607 (optional) If True, the Zeuthen flow is used for the coupling. If False, the Wilson flow is used. 608 """ 609 610 if c != 0.3: 611 raise Exception("The required lattice norm is only implemented for c=0.3 at the moment.") 612 613 plaq = _read_flow_obs(path, prefix, c, dtr_cnfg=dtr_cnfg, version="sfqcd", obspos=6, sum_t=False, Zeuthen_flow=Zeuthen_flow, integer_charge=False, **kwargs) 614 C2x1 = _read_flow_obs(path, prefix, c, dtr_cnfg=dtr_cnfg, version="sfqcd", obspos=7, sum_t=False, Zeuthen_flow=Zeuthen_flow, integer_charge=False, **kwargs) 615 L = plaq.tag["L"] 616 T = plaq.tag["T"] 617 618 if T != L: 619 raise Exception("The required lattice norm is only implemented for T=L at the moment.") 620 621 if Zeuthen_flow is not True: 622 raise Exception("The required lattice norm is only implemented for the Zeuthen flow at the moment.") 623 624 t = (c * L) ** 2 / 8 625 626 normdict = {4: 0.012341170468270, 627 6: 0.010162691462430, 628 8: 0.009031614807931, 629 10: 0.008744966371393, 630 12: 0.008650917856809, 631 14: 8.611154391267955E-03, 632 16: 0.008591758449508, 633 20: 0.008575359627103, 634 24: 0.008569387847540, 635 28: 8.566803713382559E-03, 636 32: 0.008565541650006, 637 40: 8.564480684962046E-03, 638 48: 8.564098025073460E-03, 639 64: 8.563853943383087E-03} 640 641 return t * t * (5 / 3 * plaq - 1 / 12 * C2x1) / normdict[L]
Read the gradient flow coupling based on sfqcd gradient flow measurements. See 1607.06423 for details.
Note: The current implementation only works for c=0.3 and T=L. The definition of the coupling in 1607.06423 requires projection to topological charge zero which is not done within this function but has to be performed in a separate step.
Parameters
- path (str): path of the measurement files
- prefix (str):
prefix of the measurement files, e.g.
_id0_r0.ms.dat. Ignored if file names are passed explicitly via keyword files. - c (double): Smearing radius in units of the lattice extent, c = sqrt(8 t0) / L.
- dtr_cnfg (int): (optional) parameter that specifies the number of measurements between two configs. If it is not set, the distance between two measurements in the file is assumed to be the distance between two configurations.
- steps (int): (optional) Distance between two configurations in units of trajectories / cycles. Assumed to be the distance between two measurements * dtr_cnfg if not given
- r_start (list): list which contains the first config to be read for each replicum.
- r_stop (list): list which contains the last config to be read for each replicum.
- files (list): specify the exact files that need to be read from path, practical if e.g. only one replicum is needed
- names (list): Alternative labeling for replicas/ensembles. Has to have the appropriate length.
- postfix (str): postfix of the file to read, e.g. '.gfms.dat' for openQCD-files
- Zeuthen_flow (bool): (optional) If True, the Zeuthen flow is used for the coupling. If False, the Wilson flow is used.
916def qtop_projection(qtop, target=0): 917 """Returns the projection to the topological charge sector defined by target. 918 919 Parameters 920 ---------- 921 path : Obs 922 Topological charge. 923 target : int 924 Specifies the topological sector to be reweighted to (default 0) 925 """ 926 if qtop.reweighted: 927 raise Exception('You can not use a reweighted observable for reweighting!') 928 929 proj_qtop = [] 930 for n in qtop.deltas: 931 proj_qtop.append(np.array([1 if round(qtop.value + q) == target else 0 for q in qtop.deltas[n]])) 932 933 reto = Obs(proj_qtop, qtop.names, idl=[qtop.idl[name] for name in qtop.names]) 934 reto.is_merged = qtop.is_merged 935 return reto
Returns the projection to the topological charge sector defined by target.
Parameters
- path (Obs): Topological charge.
- target (int): Specifies the topological sector to be reweighted to (default 0)
938def read_qtop_sector(path, prefix, c, target=0, **kwargs): 939 """Constructs reweighting factors to a specified topological sector. 940 941 Parameters 942 ---------- 943 path : str 944 path of the measurement files 945 prefix : str 946 prefix of the measurement files, e.g. <prefix>_id0_r0.ms.dat 947 c : double 948 Smearing radius in units of the lattice extent, c = sqrt(8 t0) / L 949 target : int 950 Specifies the topological sector to be reweighted to (default 0) 951 dtr_cnfg : int 952 (optional) parameter that specifies the number of trajectories 953 between two configs. 954 if it is not set, the distance between two measurements 955 in the file is assumed to be the distance between two configurations. 956 steps : int 957 (optional) Distance between two configurations in units of trajectories / 958 cycles. Assumed to be the distance between two measurements * dtr_cnfg if not given 959 version : str 960 version string of the openQCD (sfqcd) version used to create 961 the ensemble. Default is 2.0. May also be set to sfqcd. 962 L : int 963 spatial length of the lattice in L/a. 964 HAS to be set if version != sfqcd, since openQCD does not provide 965 this in the header 966 r_start : list 967 offset of the first ensemble, making it easier to match 968 later on with other Obs 969 r_stop : list 970 last configurations that need to be read (per replicum) 971 files : list 972 specify the exact files that need to be read 973 from path, practical if e.g. only one replicum is needed 974 names : list 975 Alternative labeling for replicas/ensembles. 976 Has to have the appropriate length 977 Zeuthen_flow : bool 978 (optional) If True, the Zeuthen flow is used for Qtop. Only possible 979 for version=='sfqcd' If False, the Wilson flow is used. 980 """ 981 982 if not isinstance(target, int): 983 raise Exception("'target' has to be an integer.") 984 985 kwargs['integer_charge'] = True 986 qtop = read_qtop(path, prefix, c, **kwargs) 987 988 return qtop_projection(qtop, target=target)
Constructs reweighting factors to a specified topological sector.
Parameters
- path (str): path of the measurement files
- prefix (str):
prefix of the measurement files, e.g.
_id0_r0.ms.dat - c (double): Smearing radius in units of the lattice extent, c = sqrt(8 t0) / L
- target (int): Specifies the topological sector to be reweighted to (default 0)
- dtr_cnfg (int): (optional) parameter that specifies the number of trajectories between two configs. if it is not set, the distance between two measurements in the file is assumed to be the distance between two configurations.
- steps (int): (optional) Distance between two configurations in units of trajectories / cycles. Assumed to be the distance between two measurements * dtr_cnfg if not given
- version (str): version string of the openQCD (sfqcd) version used to create the ensemble. Default is 2.0. May also be set to sfqcd.
- L (int): spatial length of the lattice in L/a. HAS to be set if version != sfqcd, since openQCD does not provide this in the header
- r_start (list): offset of the first ensemble, making it easier to match later on with other Obs
- r_stop (list): last configurations that need to be read (per replicum)
- files (list): specify the exact files that need to be read from path, practical if e.g. only one replicum is needed
- names (list): Alternative labeling for replicas/ensembles. Has to have the appropriate length
- Zeuthen_flow (bool): (optional) If True, the Zeuthen flow is used for Qtop. Only possible for version=='sfqcd' If False, the Wilson flow is used.