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

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
def read_qtop(path, prefix, c, dtr_cnfg=1, version='openQCD', **kwargs):
526def read_qtop(path, prefix, c, dtr_cnfg=1, version="openQCD", **kwargs):
527    """Read the topologial charge based on openQCD gradient flow measurements.
528
529    Parameters
530    ----------
531    path : str
532        path of the measurement files
533    prefix : str
534        prefix of the measurement files, e.g. <prefix>_id0_r0.ms.dat.
535        Ignored if file names are passed explicitly via keyword files.
536    c : double
537        Smearing radius in units of the lattice extent, c = sqrt(8 t0) / L.
538    dtr_cnfg : int
539        (optional) parameter that specifies the number of measurements
540        between two configs.
541        If it is not set, the distance between two measurements
542        in the file is assumed to be the distance between two configurations.
543    steps : int
544        (optional) Distance between two configurations in units of trajectories /
545         cycles. Assumed to be the distance between two measurements * dtr_cnfg if not given
546    version : str
547        Either openQCD or sfqcd, depending on the data.
548    L : int
549        spatial length of the lattice in L/a.
550        HAS to be set if version != sfqcd, since openQCD does not provide
551        this in the header
552    r_start : list
553        list which contains the first config to be read for each replicum.
554    r_stop : list
555        list which contains the last config to be read for each replicum.
556    files : list
557        specify the exact files that need to be read
558        from path, practical if e.g. only one replicum is needed
559    postfix : str
560        postfix of the file to read, e.g. '.gfms.dat' for openQCD-files
561    names : list
562        Alternative labeling for replicas/ensembles.
563        Has to have the appropriate length.
564    Zeuthen_flow : bool
565        (optional) If True, the Zeuthen flow is used for Qtop. Only possible
566        for version=='sfqcd' If False, the Wilson flow is used.
567    integer_charge : bool
568        If True, the charge is rounded towards the nearest integer on each config.
569    """
570
571    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.
def read_gf_coupling(path, prefix, c, dtr_cnfg=1, Zeuthen_flow=True, **kwargs):
574def read_gf_coupling(path, prefix, c, dtr_cnfg=1, Zeuthen_flow=True, **kwargs):
575    """Read the gradient flow coupling based on sfqcd gradient flow measurements. See 1607.06423 for details.
576
577    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.
578
579    Parameters
580    ----------
581    path : str
582        path of the measurement files
583    prefix : str
584        prefix of the measurement files, e.g. <prefix>_id0_r0.ms.dat.
585        Ignored if file names are passed explicitly via keyword files.
586    c : double
587        Smearing radius in units of the lattice extent, c = sqrt(8 t0) / L.
588    dtr_cnfg : int
589        (optional) parameter that specifies the number of measurements
590        between two configs.
591        If it is not set, the distance between two measurements
592        in the file is assumed to be the distance between two configurations.
593    steps : int
594        (optional) Distance between two configurations in units of trajectories /
595         cycles. Assumed to be the distance between two measurements * dtr_cnfg if not given
596    r_start : list
597        list which contains the first config to be read for each replicum.
598    r_stop : list
599        list which contains the last config to be read for each replicum.
600    files : list
601        specify the exact files that need to be read
602        from path, practical if e.g. only one replicum is needed
603    names : list
604        Alternative labeling for replicas/ensembles.
605        Has to have the appropriate length.
606    postfix : str
607        postfix of the file to read, e.g. '.gfms.dat' for openQCD-files
608    Zeuthen_flow : bool
609        (optional) If True, the Zeuthen flow is used for the coupling. If False, the Wilson flow is used.
610    """
611
612    if c != 0.3:
613        raise Exception("The required lattice norm is only implemented for c=0.3 at the moment.")
614
615    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)
616    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)
617    L = plaq.tag["L"]
618    T = plaq.tag["T"]
619
620    if T != L:
621        raise Exception("The required lattice norm is only implemented for T=L at the moment.")
622
623    if Zeuthen_flow is not True:
624        raise Exception("The required lattice norm is only implemented for the Zeuthen flow at the moment.")
625
626    t = (c * L) ** 2 / 8
627
628    normdict = {4: 0.012341170468270,
629                6: 0.010162691462430,
630                8: 0.009031614807931,
631                10: 0.008744966371393,
632                12: 0.008650917856809,
633                14: 8.611154391267955E-03,
634                16: 0.008591758449508,
635                20: 0.008575359627103,
636                24: 0.008569387847540,
637                28: 8.566803713382559E-03,
638                32: 0.008565541650006,
639                40: 8.564480684962046E-03,
640                48: 8.564098025073460E-03,
641                64: 8.563853943383087E-03}
642
643    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.
def qtop_projection(qtop, target=0):
918def qtop_projection(qtop, target=0):
919    """Returns the projection to the topological charge sector defined by target.
920
921    Parameters
922    ----------
923    path : Obs
924        Topological charge.
925    target : int
926        Specifies the topological sector to be reweighted to (default 0)
927    """
928    if qtop.reweighted:
929        raise Exception('You can not use a reweighted observable for reweighting!')
930
931    proj_qtop = []
932    for n in qtop.deltas:
933        proj_qtop.append(np.array([1 if round(qtop.r_values[n] + q) == target else 0 for q in qtop.deltas[n]]))
934
935    reto = Obs(proj_qtop, qtop.names, idl=[qtop.idl[name] for name in qtop.names])
936    reto.is_merged = qtop.is_merged
937    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)
def read_qtop_sector(path, prefix, c, target=0, **kwargs):
940def read_qtop_sector(path, prefix, c, target=0, **kwargs):
941    """Constructs reweighting factors to a specified topological sector.
942
943    Parameters
944    ----------
945    path : str
946        path of the measurement files
947    prefix : str
948        prefix of the measurement files, e.g. <prefix>_id0_r0.ms.dat
949    c : double
950        Smearing radius in units of the lattice extent, c = sqrt(8 t0) / L
951    target : int
952        Specifies the topological sector to be reweighted to (default 0)
953    dtr_cnfg : int
954        (optional) parameter that specifies the number of trajectories
955        between two configs.
956        if it is not set, the distance between two measurements
957        in the file is assumed to be the distance between two configurations.
958    steps : int
959        (optional) Distance between two configurations in units of trajectories /
960         cycles. Assumed to be the distance between two measurements * dtr_cnfg if not given
961    version : str
962        version string of the openQCD (sfqcd) version used to create
963        the ensemble. Default is 2.0. May also be set to sfqcd.
964    L : int
965        spatial length of the lattice in L/a.
966        HAS to be set if version != sfqcd, since openQCD does not provide
967        this in the header
968    r_start : list
969        offset of the first ensemble, making it easier to match
970        later on with other Obs
971    r_stop : list
972        last configurations that need to be read (per replicum)
973    files : list
974        specify the exact files that need to be read
975        from path, practical if e.g. only one replicum is needed
976    names : list
977        Alternative labeling for replicas/ensembles.
978        Has to have the appropriate length
979    Zeuthen_flow : bool
980        (optional) If True, the Zeuthen flow is used for Qtop. Only possible
981        for version=='sfqcd' If False, the Wilson flow is used.
982    """
983
984    if not isinstance(target, int):
985        raise Exception("'target' has to be an integer.")
986
987    kwargs['integer_charge'] = True
988    qtop = read_qtop(path, prefix, c, **kwargs)
989
990    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.
def read_ms5_xsf(path, prefix, qc, corr, sep='r', **kwargs):
 993def read_ms5_xsf(path, prefix, qc, corr, sep="r", **kwargs):
 994    """
 995    Read data from files in the specified directory with the specified prefix and quark combination extension, and return a `Corr` object containing the data.
 996
 997    Parameters
 998    ----------
 999    path : str
1000        The directory to search for the files in.
1001    prefix : str
1002        The prefix to match the files against.
1003    qc : str
1004        The quark combination extension to match the files against.
1005    corr : str
1006        The correlator to extract data for.
1007    sep : str, optional
1008        The separator to use when parsing the replika names.
1009    **kwargs
1010        Additional keyword arguments. The following keyword arguments are recognized:
1011
1012        - names (List[str]): A list of names to use for the replicas.
1013
1014    Returns
1015    -------
1016    Corr
1017        A complex valued `Corr` object containing the data read from the files. In case of boudary to bulk correlators.
1018    or
1019    CObs
1020        A complex valued `CObs` object containing the data read from the files. In case of boudary to boundary correlators.
1021
1022
1023    Raises
1024    ------
1025    FileNotFoundError
1026        If no files matching the specified prefix and quark combination extension are found in the specified directory.
1027    IOError
1028        If there is an error reading a file.
1029    struct.error
1030        If there is an error unpacking binary data.
1031    """
1032
1033    found = []
1034    files = []
1035    names = []
1036    for (dirpath, dirnames, filenames) in os.walk(path + "/"):
1037        found.extend(filenames)
1038        break
1039
1040    for f in found:
1041        if fnmatch.fnmatch(f, prefix + "*.ms5_xsf_" + qc + ".dat"):
1042            files.append(f)
1043            if not sep == "":
1044                names.append(prefix + "|r" + f.split(".")[0].split(sep)[1])
1045            else:
1046                names.append(prefix)
1047    files = sorted(files)
1048
1049    if "names" in kwargs:
1050        names = kwargs.get("names")
1051    else:
1052        names = sorted(names)
1053
1054    cnfgs = []
1055    realsamples = []
1056    imagsamples = []
1057    repnum = 0
1058    for file in files:
1059        with open(path + "/" + file, "rb") as fp:
1060
1061            t = fp.read(8)
1062            kappa = struct.unpack('d', t)[0]
1063            t = fp.read(8)
1064            csw = struct.unpack('d', t)[0]
1065            t = fp.read(8)
1066            dF = struct.unpack('d', t)[0]
1067            t = fp.read(8)
1068            zF = struct.unpack('d', t)[0]
1069
1070            t = fp.read(4)
1071            tmax = struct.unpack('i', t)[0]
1072            t = fp.read(4)
1073            bnd = struct.unpack('i', t)[0]
1074
1075            placesBI = ["gS", "gP",
1076                        "gA", "gV",
1077                        "gVt", "lA",
1078                        "lV", "lVt",
1079                        "lT", "lTt"]
1080            placesBB = ["g1", "l1"]
1081
1082            # the chunks have the following structure:
1083            # confignumber, 10x timedependent complex correlators as doubles, 2x timeindependent complex correlators as doubles
1084
1085            chunksize = 4 + (8 * 2 * tmax * 10) + (8 * 2 * 2)
1086            packstr = '=i' + ('d' * 2 * tmax * 10) + ('d' * 2 * 2)
1087            cnfgs.append([])
1088            realsamples.append([])
1089            imagsamples.append([])
1090            for t in range(tmax):
1091                realsamples[repnum].append([])
1092                imagsamples[repnum].append([])
1093
1094            while True:
1095                cnfgt = fp.read(chunksize)
1096                if not cnfgt:
1097                    break
1098                asascii = struct.unpack(packstr, cnfgt)
1099                cnfg = asascii[0]
1100                cnfgs[repnum].append(cnfg)
1101
1102                if corr not in placesBB:
1103                    tmpcorr = asascii[1 + 2 * tmax * placesBI.index(corr):1 + 2 * tmax * placesBI.index(corr) + 2 * tmax]
1104                else:
1105                    tmpcorr = asascii[1 + 2 * tmax * len(placesBI) + 2 * placesBB.index(corr):1 + 2 * tmax * len(placesBI) + 2 * placesBB.index(corr) + 2]
1106
1107                corrres = [[], []]
1108                for i in range(len(tmpcorr)):
1109                    corrres[i % 2].append(tmpcorr[i])
1110                for t in range(int(len(tmpcorr) / 2)):
1111                    realsamples[repnum][t].append(corrres[0][t])
1112                for t in range(int(len(tmpcorr) / 2)):
1113                    imagsamples[repnum][t].append(corrres[1][t])
1114        repnum += 1
1115
1116    s = "Read correlator " + corr + " from " + str(repnum) + " replika with " + str(len(realsamples[0][t]))
1117    for rep in range(1, repnum):
1118        s += ", " + str(len(realsamples[rep][t]))
1119    s += " samples"
1120    print(s)
1121    print("Asserted run parameters:\n T:", tmax, "kappa:", kappa, "csw:", csw, "dF:", dF, "zF:", zF, "bnd:", bnd)
1122
1123    # we have the data now... but we need to re format the whole thing and put it into Corr objects.
1124
1125    compObs = []
1126
1127    for t in range(int(len(tmpcorr) / 2)):
1128        compObs.append(CObs(Obs([realsamples[rep][t] for rep in range(repnum)], names=names, idl=cnfgs),
1129                            Obs([imagsamples[rep][t] for rep in range(repnum)], names=names, idl=cnfgs)))
1130
1131    if len(compObs) == 1:
1132        return compObs[0]
1133    else:
1134        return Corr(compObs)

Read data from files in the specified directory with the specified prefix and quark combination extension, and return a Corr object containing the data.

Parameters
  • path (str): The directory to search for the files in.
  • prefix (str): The prefix to match the files against.
  • qc (str): The quark combination extension to match the files against.
  • corr (str): The correlator to extract data for.
  • sep (str, optional): The separator to use when parsing the replika names.
  • **kwargs: Additional keyword arguments. The following keyword arguments are recognized:

    • names (List[str]): A list of names to use for the replicas.
Returns
  • Corr: A complex valued Corr object containing the data read from the files. In case of boudary to bulk correlators.
  • or
  • CObs: A complex valued CObs object containing the data read from the files. In case of boudary to boundary correlators.
Raises
  • FileNotFoundError: If no files matching the specified prefix and quark combination extension are found in the specified directory.
  • IOError: If there is an error reading a file.
  • struct.error: If there is an error unpacking binary data.