pyerrors.input.openQCD

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

Extract t0/a^2 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 - c (where c=0.3 by default) 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. The function also assumes that r_step is the same across all replica.

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)
  • postfix (str): Postfix of measurement file (Default: ms)
  • c (float): Constant that defines the flow scale. Default 0.3 for t_0, choose 2./3 for t_1.
  • 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
Returns
  • t0 (Obs): Extracted t0
def extract_w0( path, prefix, dtr_read, xmin, spatial_extent, fit_range=5, postfix='ms', c=0.3, **kwargs):
508def extract_w0(path, prefix, dtr_read, xmin, spatial_extent, fit_range=5, postfix='ms', c=0.3, **kwargs):
509    """Extract w0/a from given .ms.dat files. Returns w0 as Obs.
510
511    It is assumed that all boundary effects have
512    sufficiently decayed at x0=xmin.
513    The data around the zero crossing of t d(t^2<E>)/dt -  (where c=0.3 by default)
514    is fitted with a linear function
515    from which the exact root is extracted.
516
517    It is assumed that one measurement is performed for each config.
518    If this is not the case, the resulting idl, as well as the handling
519    of r_start, r_stop and r_step is wrong and the user has to correct
520    this in the resulting observable.
521
522    Parameters
523    ----------
524    path : str
525        Path to .ms.dat files
526    prefix : str
527        Ensemble prefix
528    dtr_read : int
529        Determines how many trajectories should be skipped
530        when reading the ms.dat files.
531        Corresponds to dtr_cnfg / dtr_ms in the openQCD input file.
532    xmin : int
533        First timeslice where the boundary
534        effects have sufficiently decayed.
535    spatial_extent : int
536        spatial extent of the lattice, required for normalization.
537    fit_range : int
538        Number of data points left and right of the zero
539        crossing to be included in the linear fit. (Default: 5)
540    postfix : str
541        Postfix of measurement file (Default: ms)
542    c: float
543        Constant that defines the flow scale. Default 0.3 for w_0, choose 2./3 for w_1.
544    r_start : list
545        list which contains the first config to be read for each replicum.
546    r_stop : list
547        list which contains the last config to be read for each replicum.
548    r_step : int
549        integer that defines a fixed step size between two measurements (in units of configs)
550        If not given, r_step=1 is assumed.
551    plaquette : bool
552        If true extract the plaquette estimate of w0 instead.
553    names : list
554        list of names that is assigned to the data according according
555        to the order in the file list. Use careful, if you do not provide file names!
556    files : list
557        list which contains the filenames to be read. No automatic detection of
558        files performed if given.
559    plot_fit : bool
560        If true, the fit for the extraction of w0 is shown together with the data.
561    assume_thermalization : bool
562        If True: If the first record divided by the distance between two measurements is larger than
563        1, it is assumed that this is due to thermalization and the first measurement belongs
564        to the first config (default).
565        If False: The config numbers are assumed to be traj_number // difference
566
567    Returns
568    -------
569    w0 : Obs
570        Extracted w0
571    """
572
573    E_dict = _extract_flowed_energy_density(path, prefix, dtr_read, xmin, spatial_extent, postfix, **kwargs)
574
575    ftimes = sorted(E_dict.keys())
576
577    t2E_dict = {}
578    for t in ftimes:
579        t2E_dict[t] = t ** 2 * E_dict[t]
580
581    tdtt2E_dict = {}
582    tdtt2E_dict[ftimes[0]] = ftimes[0] * (t2E_dict[ftimes[1]] - t2E_dict[ftimes[0]]) / (ftimes[1] - ftimes[0]) - c
583    for i in range(1, len(ftimes) - 1):
584        tdtt2E_dict[ftimes[i]] = ftimes[i] * (t2E_dict[ftimes[i + 1]] - t2E_dict[ftimes[i - 1]]) / (ftimes[i + 1] - ftimes[i - 1]) - c
585    tdtt2E_dict[ftimes[-1]] = ftimes[-1] * (t2E_dict[ftimes[-1]] - t2E_dict[ftimes[-2]]) / (ftimes[-1] - ftimes[-2]) - c
586
587    return np.sqrt(fit_t0(tdtt2E_dict, fit_range, plot_fit=kwargs.get('plot_fit'), observable='w0'))

Extract w0/a from given .ms.dat files. Returns w0 as Obs.

It is assumed that all boundary effects have sufficiently decayed at x0=xmin. The data around the zero crossing of t d(t^2)/dt - (where c=0.3 by default) 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)
  • postfix (str): Postfix of measurement file (Default: ms)
  • c (float): Constant that defines the flow scale. Default 0.3 for w_0, choose 2./3 for w_1.
  • 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 w0 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 w0 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
Returns
  • w0 (Obs): Extracted w0
def read_qtop(path, prefix, c, dtr_cnfg=1, version='openQCD', **kwargs):
677def read_qtop(path, prefix, c, dtr_cnfg=1, version="openQCD", **kwargs):
678    """Read the topologial charge based on openQCD gradient flow measurements.
679
680    Parameters
681    ----------
682    path : str
683        path of the measurement files
684    prefix : str
685        prefix of the measurement files, e.g. <prefix>_id0_r0.ms.dat.
686        Ignored if file names are passed explicitly via keyword files.
687    c : double
688        Smearing radius in units of the lattice extent, c = sqrt(8 t0) / L.
689    dtr_cnfg : int
690        (optional) parameter that specifies the number of measurements
691        between two configs.
692        If it is not set, the distance between two measurements
693        in the file is assumed to be the distance between two configurations.
694    steps : int
695        (optional) Distance between two configurations in units of trajectories /
696         cycles. Assumed to be the distance between two measurements * dtr_cnfg if not given
697    version : str
698        Either openQCD or sfqcd, depending on the data.
699    L : int
700        spatial length of the lattice in L/a.
701        HAS to be set if version != sfqcd, since openQCD does not provide
702        this in the header
703    r_start : list
704        list which contains the first config to be read for each replicum.
705    r_stop : list
706        list which contains the last config to be read for each replicum.
707    files : list
708        specify the exact files that need to be read
709        from path, practical if e.g. only one replicum is needed
710    postfix : str
711        postfix of the file to read, e.g. '.gfms.dat' for openQCD-files
712    names : list
713        Alternative labeling for replicas/ensembles.
714        Has to have the appropriate length.
715    Zeuthen_flow : bool
716        (optional) If True, the Zeuthen flow is used for Qtop. Only possible
717        for version=='sfqcd' If False, the Wilson flow is used.
718    integer_charge : bool
719        If True, the charge is rounded towards the nearest integer on each config.
720
721    Returns
722    -------
723    result : Obs
724        Read topological charge
725    """
726
727    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.
Returns
  • result (Obs): Read topological charge
def read_gf_coupling(path, prefix, c, dtr_cnfg=1, Zeuthen_flow=True, **kwargs):
730def read_gf_coupling(path, prefix, c, dtr_cnfg=1, Zeuthen_flow=True, **kwargs):
731    """Read the gradient flow coupling based on sfqcd gradient flow measurements. See 1607.06423 for details.
732
733    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.
734
735    Parameters
736    ----------
737    path : str
738        path of the measurement files
739    prefix : str
740        prefix of the measurement files, e.g. <prefix>_id0_r0.ms.dat.
741        Ignored if file names are passed explicitly via keyword files.
742    c : double
743        Smearing radius in units of the lattice extent, c = sqrt(8 t0) / L.
744    dtr_cnfg : int
745        (optional) parameter that specifies the number of measurements
746        between two configs.
747        If it is not set, the distance between two measurements
748        in the file is assumed to be the distance between two configurations.
749    steps : int
750        (optional) Distance between two configurations in units of trajectories /
751         cycles. Assumed to be the distance between two measurements * dtr_cnfg if not given
752    r_start : list
753        list which contains the first config to be read for each replicum.
754    r_stop : list
755        list which contains the last config to be read for each replicum.
756    files : list
757        specify the exact files that need to be read
758        from path, practical if e.g. only one replicum is needed
759    names : list
760        Alternative labeling for replicas/ensembles.
761        Has to have the appropriate length.
762    postfix : str
763        postfix of the file to read, e.g. '.gfms.dat' for openQCD-files
764    Zeuthen_flow : bool
765        (optional) If True, the Zeuthen flow is used for the coupling. If False, the Wilson flow is used.
766    """
767
768    if c != 0.3:
769        raise Exception("The required lattice norm is only implemented for c=0.3 at the moment.")
770
771    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)
772    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)
773    L = plaq.tag["L"]
774    T = plaq.tag["T"]
775
776    if T != L:
777        raise Exception("The required lattice norm is only implemented for T=L at the moment.")
778
779    if Zeuthen_flow is not True:
780        raise Exception("The required lattice norm is only implemented for the Zeuthen flow at the moment.")
781
782    t = (c * L) ** 2 / 8
783
784    normdict = {4: 0.012341170468270,
785                6: 0.010162691462430,
786                8: 0.009031614807931,
787                10: 0.008744966371393,
788                12: 0.008650917856809,
789                14: 8.611154391267955E-03,
790                16: 0.008591758449508,
791                20: 0.008575359627103,
792                24: 0.008569387847540,
793                28: 8.566803713382559E-03,
794                32: 0.008565541650006,
795                40: 8.564480684962046E-03,
796                48: 8.564098025073460E-03,
797                64: 8.563853943383087E-03}
798
799    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):
1075def qtop_projection(qtop, target=0):
1076    """Returns the projection to the topological charge sector defined by target.
1077
1078    Parameters
1079    ----------
1080    path : Obs
1081        Topological charge.
1082    target : int
1083        Specifies the topological sector to be reweighted to (default 0)
1084
1085    Returns
1086    -------
1087    reto : Obs
1088        projection to the topological charge sector defined by target
1089    """
1090    if qtop.reweighted:
1091        raise Exception('You can not use a reweighted observable for reweighting!')
1092
1093    proj_qtop = []
1094    for n in qtop.deltas:
1095        proj_qtop.append(np.array([1 if round(qtop.r_values[n] + q) == target else 0 for q in qtop.deltas[n]]))
1096
1097    reto = Obs(proj_qtop, qtop.names, idl=[qtop.idl[name] for name in qtop.names])
1098    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)
Returns
  • reto (Obs): projection to the topological charge sector defined by target
def read_qtop_sector(path, prefix, c, target=0, **kwargs):
1101def read_qtop_sector(path, prefix, c, target=0, **kwargs):
1102    """Constructs reweighting factors to a specified topological sector.
1103
1104    Parameters
1105    ----------
1106    path : str
1107        path of the measurement files
1108    prefix : str
1109        prefix of the measurement files, e.g. <prefix>_id0_r0.ms.dat
1110    c : double
1111        Smearing radius in units of the lattice extent, c = sqrt(8 t0) / L
1112    target : int
1113        Specifies the topological sector to be reweighted to (default 0)
1114    dtr_cnfg : int
1115        (optional) parameter that specifies the number of trajectories
1116        between two configs.
1117        if it is not set, the distance between two measurements
1118        in the file is assumed to be the distance between two configurations.
1119    steps : int
1120        (optional) Distance between two configurations in units of trajectories /
1121         cycles. Assumed to be the distance between two measurements * dtr_cnfg if not given
1122    version : str
1123        version string of the openQCD (sfqcd) version used to create
1124        the ensemble. Default is 2.0. May also be set to sfqcd.
1125    L : int
1126        spatial length of the lattice in L/a.
1127        HAS to be set if version != sfqcd, since openQCD does not provide
1128        this in the header
1129    r_start : list
1130        offset of the first ensemble, making it easier to match
1131        later on with other Obs
1132    r_stop : list
1133        last configurations that need to be read (per replicum)
1134    files : list
1135        specify the exact files that need to be read
1136        from path, practical if e.g. only one replicum is needed
1137    names : list
1138        Alternative labeling for replicas/ensembles.
1139        Has to have the appropriate length
1140    Zeuthen_flow : bool
1141        (optional) If True, the Zeuthen flow is used for Qtop. Only possible
1142        for version=='sfqcd' If False, the Wilson flow is used.
1143
1144    Returns
1145    -------
1146    reto : Obs
1147        projection to the topological charge sector defined by target
1148    """
1149
1150    if not isinstance(target, int):
1151        raise Exception("'target' has to be an integer.")
1152
1153    kwargs['integer_charge'] = True
1154    qtop = read_qtop(path, prefix, c, **kwargs)
1155
1156    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.
Returns
  • reto (Obs): projection to the topological charge sector defined by target
def read_ms5_xsf(path, prefix, qc, corr, sep='r', **kwargs):
1159def read_ms5_xsf(path, prefix, qc, corr, sep="r", **kwargs):
1160    """
1161    Read data from files in the specified directory with the specified prefix and quark combination extension, and return a `Corr` object containing the data.
1162
1163    Parameters
1164    ----------
1165    path : str
1166        The directory to search for the files in.
1167    prefix : str
1168        The prefix to match the files against.
1169    qc : str
1170        The quark combination extension to match the files against.
1171    corr : str
1172        The correlator to extract data for.
1173    sep : str, optional
1174        The separator to use when parsing the replika names.
1175    **kwargs
1176        Additional keyword arguments. The following keyword arguments are recognized:
1177
1178        - names (List[str]): A list of names to use for the replicas.
1179        - files (List[str]): A list of files to read data from.
1180        - idl (List[List[int]]): A list of idls per replicum, resticting data to the idls given.
1181
1182    Returns
1183    -------
1184    Corr
1185        A complex valued `Corr` object containing the data read from the files. In case of boudary to bulk correlators.
1186    or
1187    CObs
1188        A complex valued `CObs` object containing the data read from the files. In case of boudary to boundary correlators.
1189
1190
1191    Raises
1192    ------
1193    FileNotFoundError
1194        If no files matching the specified prefix and quark combination extension are found in the specified directory.
1195    IOError
1196        If there is an error reading a file.
1197    struct.error
1198        If there is an error unpacking binary data.
1199    """
1200
1201    # found = []
1202    files = []
1203    names = []
1204
1205    # test if the input is correct
1206    if qc not in ['dd', 'ud', 'du', 'uu']:
1207        raise Exception("Unknown quark conbination!")
1208
1209    if corr not in ["gS", "gP", "gA", "gV", "gVt", "lA", "lV", "lVt", "lT", "lTt", "g1", "l1"]:
1210        raise Exception("Unknown correlator!")
1211
1212    if "files" in kwargs:
1213        known_files = kwargs.get("files")
1214    else:
1215        known_files = []
1216    files = _find_files(path, prefix, "ms5_xsf_" + qc, "dat", known_files=known_files)
1217
1218    if "names" in kwargs:
1219        names = kwargs.get("names")
1220    else:
1221        for f in files:
1222            if not sep == "":
1223                se = f.split(".")[0]
1224                for s in f.split(".")[1:-2]:
1225                    se += "." + s
1226                names.append(se.split(sep)[0] + "|r" + se.split(sep)[1])
1227            else:
1228                names.append(prefix)
1229    if 'idl' in kwargs:
1230        expected_idl = kwargs.get('idl')
1231    names = sorted(names)
1232    files = sorted(files)
1233
1234    cnfgs = []
1235    realsamples = []
1236    imagsamples = []
1237    repnum = 0
1238    for file in files:
1239        with open(path + "/" + file, "rb") as fp:
1240
1241            t = fp.read(8)
1242            kappa = struct.unpack('d', t)[0]
1243            t = fp.read(8)
1244            csw = struct.unpack('d', t)[0]
1245            t = fp.read(8)
1246            dF = struct.unpack('d', t)[0]
1247            t = fp.read(8)
1248            zF = struct.unpack('d', t)[0]
1249
1250            t = fp.read(4)
1251            tmax = struct.unpack('i', t)[0]
1252            t = fp.read(4)
1253            bnd = struct.unpack('i', t)[0]
1254
1255            placesBI = ["gS", "gP",
1256                        "gA", "gV",
1257                        "gVt", "lA",
1258                        "lV", "lVt",
1259                        "lT", "lTt"]
1260            placesBB = ["g1", "l1"]
1261
1262            # the chunks have the following structure:
1263            # confignumber, 10x timedependent complex correlators as doubles, 2x timeindependent complex correlators as doubles
1264
1265            chunksize = 4 + (8 * 2 * tmax * 10) + (8 * 2 * 2)
1266            packstr = '=i' + ('d' * 2 * tmax * 10) + ('d' * 2 * 2)
1267            cnfgs.append([])
1268            realsamples.append([])
1269            imagsamples.append([])
1270            for _ in range(tmax):
1271                realsamples[repnum].append([])
1272                imagsamples[repnum].append([])
1273            if 'idl' in kwargs:
1274                left_idl = set(expected_idl[repnum])
1275            while True:
1276                cnfgt = fp.read(chunksize)
1277                if not cnfgt:
1278                    break
1279                asascii = struct.unpack(packstr, cnfgt)
1280                cnfg = asascii[0]
1281                idl_wanted = True
1282                if 'idl' in kwargs:
1283                    idl_wanted = (cnfg in expected_idl[repnum])
1284                    left_idl = left_idl - set([cnfg])
1285                if idl_wanted:
1286                    cnfgs[repnum].append(cnfg)
1287
1288                    if corr not in placesBB:
1289                        tmpcorr = asascii[1 + 2 * tmax * placesBI.index(corr):1 + 2 * tmax * placesBI.index(corr) + 2 * tmax]
1290                    else:
1291                        tmpcorr = asascii[1 + 2 * tmax * len(placesBI) + 2 * placesBB.index(corr):1 + 2 * tmax * len(placesBI) + 2 * placesBB.index(corr) + 2]
1292
1293                    corrres = [[], []]
1294                    for i in range(len(tmpcorr)):
1295                        corrres[i % 2].append(tmpcorr[i])
1296                    for t in range(int(len(tmpcorr) / 2)):
1297                        realsamples[repnum][t].append(corrres[0][t])
1298                    for t in range(int(len(tmpcorr) / 2)):
1299                        imagsamples[repnum][t].append(corrres[1][t])
1300            if 'idl' in kwargs:
1301                left_idl = list(left_idl)
1302                if expected_idl[repnum] == left_idl:
1303                    raise ValueError("None of the idls searched for were found in replikum of file " + file)
1304                elif len(left_idl) > 0:
1305                    warnings.warn('Could not find idls ' + str(left_idl) + ' in replikum of file ' + file, UserWarning, stacklevel=2)
1306        repnum += 1
1307    s = "Read correlator " + corr + " from " + str(repnum) + " replika with idls" + str(realsamples[0][t])
1308    for rep in range(1, repnum):
1309        s += ", " + str(realsamples[rep][t])
1310    print(s)
1311    print("Asserted run parameters:\n T:", tmax, "kappa:", kappa, "csw:", csw, "dF:", dF, "zF:", zF, "bnd:", bnd)
1312
1313    # we have the data now... but we need to re format the whole thing and put it into Corr objects.
1314
1315    compObs = []
1316
1317    for t in range(int(len(tmpcorr) / 2)):
1318        compObs.append(CObs(Obs([realsamples[rep][t] for rep in range(repnum)], names=names, idl=cnfgs),
1319                            Obs([imagsamples[rep][t] for rep in range(repnum)], names=names, idl=cnfgs)))
1320
1321    if len(compObs) == 1:
1322        return compObs[0]
1323    else:
1324        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.
    • files (List[str]): A list of files to read data from.
    • idl (List[List[int]]): A list of idls per replicum, resticting data to the idls given.
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.