pyerrors.input.openQCD

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

Read rwms format from given folder structure. Returns a list of length nrw

Parameters
  • path (str): path that contains the data files
  • prefix (str): all files in path that start with prefix are considered as input files. May be used together postfix to consider only special file endings. Prefix is ignored, if the keyword 'files' is used.
  • version (str): version of openQCD, default 2.0
  • names (list): list of names that is assigned to the data according according to the order in the file list. Use careful, if you do not provide file names!
  • r_start (list): list which contains the first config to be read for each replicum
  • r_stop (list): list which contains the last config to be read for each replicum
  • r_step (int): integer that defines a fixed step size between two measurements (in units of configs) If not given, r_step=1 is assumed.
  • postfix (str): postfix of the file to read, e.g. '.ms1' for openQCD-files
  • files (list): list which contains the filenames to be read. No automatic detection of files performed if given.
  • print_err (bool): Print additional information that is useful for debugging.
def extract_t0(path, prefix, dtr_read, xmin, spatial_extent, fit_range=5, **kwargs):
233def extract_t0(path, prefix, dtr_read, xmin, spatial_extent, fit_range=5, **kwargs):
234    """Extract t0 from given .ms.dat files. Returns t0 as Obs.
235
236    It is assumed that all boundary effects have
237    sufficiently decayed at x0=xmin.
238    The data around the zero crossing of t^2<E> - 0.3
239    is fitted with a linear function
240    from which the exact root is extracted.
241
242    It is assumed that one measurement is performed for each config.
243    If this is not the case, the resulting idl, as well as the handling
244    of r_start, r_stop and r_step is wrong and the user has to correct
245    this in the resulting observable.
246
247    Parameters
248    ----------
249    path : str
250        Path to .ms.dat files
251    prefix : str
252        Ensemble prefix
253    dtr_read : int
254        Determines how many trajectories should be skipped
255        when reading the ms.dat files.
256        Corresponds to dtr_cnfg / dtr_ms in the openQCD input file.
257    xmin : int
258        First timeslice where the boundary
259        effects have sufficiently decayed.
260    spatial_extent : int
261        spatial extent of the lattice, required for normalization.
262    fit_range : int
263        Number of data points left and right of the zero
264        crossing to be included in the linear fit. (Default: 5)
265    r_start : list
266        list which contains the first config to be read for each replicum.
267    r_stop : list
268        list which contains the last config to be read for each replicum.
269    r_step : int
270        integer that defines a fixed step size between two measurements (in units of configs)
271        If not given, r_step=1 is assumed.
272    plaquette : bool
273        If true extract the plaquette estimate of t0 instead.
274    names : list
275        list of names that is assigned to the data according according
276        to the order in the file list. Use careful, if you do not provide file names!
277    files : list
278        list which contains the filenames to be read. No automatic detection of
279        files performed if given.
280    plot_fit : bool
281        If true, the fit for the extraction of t0 is shown together with the data.
282    assume_thermalization : bool
283        If True: If the first record divided by the distance between two measurements is larger than
284        1, it is assumed that this is due to thermalization and the first measurement belongs
285        to the first config (default).
286        If False: The config numbers are assumed to be traj_number // difference
287    """
288
289    ls = []
290    for (dirpath, dirnames, filenames) in os.walk(path):
291        ls.extend(filenames)
292        break
293
294    if not ls:
295        raise Exception('Error, directory not found')
296
297    if 'files' in kwargs:
298        ls = kwargs.get('files')
299    else:
300        for exc in ls:
301            if not fnmatch.fnmatch(exc, prefix + '*.ms.dat'):
302                ls = list(set(ls) - set([exc]))
303        if len(ls) > 1:
304            ls.sort(key=lambda x: int(re.findall(r'\d+', x[len(prefix):])[0]))
305    replica = len(ls)
306
307    if 'r_start' in kwargs:
308        r_start = kwargs.get('r_start')
309        if len(r_start) != replica:
310            raise Exception('r_start does not match number of replicas')
311        r_start = [o if o else None for o in r_start]
312    else:
313        r_start = [None] * replica
314
315    if 'r_stop' in kwargs:
316        r_stop = kwargs.get('r_stop')
317        if len(r_stop) != replica:
318            raise Exception('r_stop does not match number of replicas')
319    else:
320        r_stop = [None] * replica
321
322    if 'r_step' in kwargs:
323        r_step = kwargs.get('r_step')
324    else:
325        r_step = 1
326
327    print('Extract t0 from', prefix, ',', replica, 'replica')
328
329    if 'names' in kwargs:
330        rep_names = kwargs.get('names')
331    else:
332        rep_names = []
333        for entry in ls:
334            truncated_entry = entry.split('.')[0]
335            idx = truncated_entry.index('r')
336            rep_names.append(truncated_entry[:idx] + '|' + truncated_entry[idx:])
337
338    Ysum = []
339
340    configlist = []
341    r_start_index = []
342    r_stop_index = []
343
344    for rep in range(replica):
345
346        with open(path + '/' + ls[rep], 'rb') as fp:
347            t = fp.read(12)
348            header = struct.unpack('iii', t)
349            if rep == 0:
350                dn = header[0]
351                nn = header[1]
352                tmax = header[2]
353            elif dn != header[0] or nn != header[1] or tmax != header[2]:
354                raise Exception('Replica parameters do not match.')
355
356            t = fp.read(8)
357            if rep == 0:
358                eps = struct.unpack('d', t)[0]
359                print('Step size:', eps, ', Maximal t value:', dn * (nn) * eps)
360            elif eps != struct.unpack('d', t)[0]:
361                raise Exception('Values for eps do not match among replica.')
362
363            Ysl = []
364
365            configlist.append([])
366            while True:
367                t = fp.read(4)
368                if (len(t) < 4):
369                    break
370                nc = struct.unpack('i', t)[0]
371                configlist[-1].append(nc)
372
373                t = fp.read(8 * tmax * (nn + 1))
374                if kwargs.get('plaquette'):
375                    if nc % dtr_read == 0:
376                        Ysl.append(struct.unpack('d' * tmax * (nn + 1), t))
377                t = fp.read(8 * tmax * (nn + 1))
378                if not kwargs.get('plaquette'):
379                    if nc % dtr_read == 0:
380                        Ysl.append(struct.unpack('d' * tmax * (nn + 1), t))
381                t = fp.read(8 * tmax * (nn + 1))
382
383        Ysum.append([])
384        for i, item in enumerate(Ysl):
385            Ysum[-1].append([np.mean(item[current + xmin:
386                             current + tmax - xmin])
387                            for current in range(0, len(item), tmax)])
388
389        diffmeas = configlist[-1][-1] - configlist[-1][-2]
390        configlist[-1] = [item // diffmeas for item in configlist[-1]]
391        if kwargs.get('assume_thermalization', True) and configlist[-1][0] > 1:
392            warnings.warn('Assume thermalization and that the first measurement belongs to the first config.')
393            offset = configlist[-1][0] - 1
394            configlist[-1] = [item - offset for item in configlist[-1]]
395
396        if r_start[rep] is None:
397            r_start_index.append(0)
398        else:
399            try:
400                r_start_index.append(configlist[-1].index(r_start[rep]))
401            except ValueError:
402                raise Exception('Config %d not in file with range [%d, %d]' % (
403                    r_start[rep], configlist[-1][0], configlist[-1][-1])) from None
404
405        if r_stop[rep] is None:
406            r_stop_index.append(len(configlist[-1]) - 1)
407        else:
408            try:
409                r_stop_index.append(configlist[-1].index(r_stop[rep]))
410            except ValueError:
411                raise Exception('Config %d not in file with range [%d, %d]' % (
412                    r_stop[rep], configlist[-1][0], configlist[-1][-1])) from None
413
414    if np.any([len(np.unique(np.diff(cl))) != 1 for cl in configlist]):
415        raise Exception('Irregular spaced data in input file!', [len(np.unique(np.diff(cl))) for cl in configlist])
416    stepsizes = [list(np.unique(np.diff(cl)))[0] for cl in configlist]
417    if np.any([step != 1 for step in stepsizes]):
418        warnings.warn('Stepsize between configurations is greater than one!' + str(stepsizes), RuntimeWarning)
419
420    idl = [range(configlist[rep][r_start_index[rep]], configlist[rep][r_stop_index[rep]] + 1, r_step) for rep in range(replica)]
421    t2E_dict = {}
422    for n in range(nn + 1):
423        samples = []
424        for nrep, rep in enumerate(Ysum):
425            samples.append([])
426            for cnfg in rep:
427                samples[-1].append(cnfg[n])
428            samples[-1] = samples[-1][r_start_index[nrep]:r_stop_index[nrep] + 1][::r_step]
429        new_obs = Obs(samples, rep_names, idl=idl)
430        t2E_dict[n * dn * eps] = (n * dn * eps) ** 2 * new_obs / (spatial_extent ** 3) - 0.3
431
432    zero_crossing = np.argmax(np.array(
433        [o.value for o in t2E_dict.values()]) > 0.0)
434
435    x = list(t2E_dict.keys())[zero_crossing - fit_range:
436                              zero_crossing + fit_range]
437    y = list(t2E_dict.values())[zero_crossing - fit_range:
438                                zero_crossing + fit_range]
439    [o.gamma_method() for o in y]
440
441    fit_result = fit_lin(x, y)
442
443    if kwargs.get('plot_fit'):
444        plt.figure()
445        gs = gridspec.GridSpec(2, 1, height_ratios=[3, 1], wspace=0.0, hspace=0.0)
446        ax0 = plt.subplot(gs[0])
447        xmore = list(t2E_dict.keys())[zero_crossing - fit_range - 2: zero_crossing + fit_range + 2]
448        ymore = list(t2E_dict.values())[zero_crossing - fit_range - 2: zero_crossing + fit_range + 2]
449        [o.gamma_method() for o in ymore]
450        ax0.errorbar(xmore, [yi.value for yi in ymore], yerr=[yi.dvalue for yi in ymore], fmt='x')
451        xplot = np.linspace(np.min(x), np.max(x))
452        yplot = [fit_result[0] + fit_result[1] * xi for xi in xplot]
453        [yi.gamma_method() for yi in yplot]
454        ax0.fill_between(xplot, y1=[yi.value - yi.dvalue for yi in yplot], y2=[yi.value + yi.dvalue for yi in yplot])
455        retval = (-fit_result[0] / fit_result[1])
456        retval.gamma_method()
457        ylim = ax0.get_ylim()
458        ax0.fill_betweenx(ylim, x1=retval.value - retval.dvalue, x2=retval.value + retval.dvalue, color='gray', alpha=0.4)
459        ax0.set_ylim(ylim)
460        ax0.set_ylabel(r'$t^2 \langle E(t) \rangle - 0.3 $')
461        xlim = ax0.get_xlim()
462
463        fit_res = [fit_result[0] + fit_result[1] * xi for xi in x]
464        residuals = (np.asarray([o.value for o in y]) - [o.value for o in fit_res]) / np.asarray([o.dvalue for o in y])
465        ax1 = plt.subplot(gs[1])
466        ax1.plot(x, residuals, 'ko', ls='none', markersize=5)
467        ax1.tick_params(direction='out')
468        ax1.tick_params(axis="x", bottom=True, top=True, labelbottom=True)
469        ax1.axhline(y=0.0, ls='--', color='k')
470        ax1.fill_between(xlim, -1.0, 1.0, alpha=0.1, facecolor='k')
471        ax1.set_xlim(xlim)
472        ax1.set_ylabel('Residuals')
473        ax1.set_xlabel(r'$t/a^2$')
474
475        plt.draw()
476    return -fit_result[0] / fit_result[1]

Extract t0 from given .ms.dat files. Returns t0 as Obs.

It is assumed that all boundary effects have sufficiently decayed at x0=xmin. The data around the zero crossing of t^2 - 0.3 is fitted with a linear function from which the exact root is extracted.

It is assumed that one measurement is performed for each config. If this is not the case, the resulting idl, as well as the handling of r_start, r_stop and r_step is wrong and the user has to correct this in the resulting observable.

Parameters
  • path (str): Path to .ms.dat files
  • prefix (str): Ensemble prefix
  • dtr_read (int): Determines how many trajectories should be skipped when reading the ms.dat files. Corresponds to dtr_cnfg / dtr_ms in the openQCD input file.
  • xmin (int): First timeslice where the boundary effects have sufficiently decayed.
  • spatial_extent (int): spatial extent of the lattice, required for normalization.
  • fit_range (int): Number of data points left and right of the zero crossing to be included in the linear fit. (Default: 5)
  • r_start (list): list which contains the first config to be read for each replicum.
  • r_stop (list): list which contains the last config to be read for each replicum.
  • r_step (int): integer that defines a fixed step size between two measurements (in units of configs) If not given, r_step=1 is assumed.
  • plaquette (bool): If true extract the plaquette estimate of t0 instead.
  • names (list): list of names that is assigned to the data according according to the order in the file list. Use careful, if you do not provide file names!
  • files (list): list which contains the filenames to be read. No automatic detection of files performed if given.
  • plot_fit (bool): If true, the fit for the extraction of t0 is shown together with the data.
  • assume_thermalization (bool): If True: If the first record divided by the distance between two measurements is larger than 1, it is assumed that this is due to thermalization and the first measurement belongs to the first config (default). If False: The config numbers are assumed to be traj_number // difference
def read_qtop(path, prefix, c, dtr_cnfg=1, version='openQCD', **kwargs):
524def read_qtop(path, prefix, c, dtr_cnfg=1, version="openQCD", **kwargs):
525    """Read the topologial charge based on openQCD gradient flow measurements.
526
527    Parameters
528    ----------
529    path : str
530        path of the measurement files
531    prefix : str
532        prefix of the measurement files, e.g. <prefix>_id0_r0.ms.dat.
533        Ignored if file names are passed explicitly via keyword files.
534    c : double
535        Smearing radius in units of the lattice extent, c = sqrt(8 t0) / L.
536    dtr_cnfg : int
537        (optional) parameter that specifies the number of measurements
538        between two configs.
539        If it is not set, the distance between two measurements
540        in the file is assumed to be the distance between two configurations.
541    steps : int
542        (optional) Distance between two configurations in units of trajectories /
543         cycles. Assumed to be the distance between two measurements * dtr_cnfg if not given
544    version : str
545        Either openQCD or sfqcd, depending on the data.
546    L : int
547        spatial length of the lattice in L/a.
548        HAS to be set if version != sfqcd, since openQCD does not provide
549        this in the header
550    r_start : list
551        list which contains the first config to be read for each replicum.
552    r_stop : list
553        list which contains the last config to be read for each replicum.
554    files : list
555        specify the exact files that need to be read
556        from path, practical if e.g. only one replicum is needed
557    postfix : str
558        postfix of the file to read, e.g. '.gfms.dat' for openQCD-files
559    names : list
560        Alternative labeling for replicas/ensembles.
561        Has to have the appropriate length.
562    Zeuthen_flow : bool
563        (optional) If True, the Zeuthen flow is used for Qtop. Only possible
564        for version=='sfqcd' If False, the Wilson flow is used.
565    integer_charge : bool
566        If True, the charge is rounded towards the nearest integer on each config.
567    """
568
569    return _read_flow_obs(path, prefix, c, dtr_cnfg=dtr_cnfg, version=version, obspos=0, **kwargs)

Read the topologial charge based on openQCD gradient flow measurements.

Parameters
  • path (str): path of the measurement files
  • prefix (str): prefix of the measurement files, e.g. _id0_r0.ms.dat. Ignored if file names are passed explicitly via keyword files.
  • c (double): Smearing radius in units of the lattice extent, c = sqrt(8 t0) / L.
  • dtr_cnfg (int): (optional) parameter that specifies the number of measurements between two configs. If it is not set, the distance between two measurements in the file is assumed to be the distance between two configurations.
  • steps (int): (optional) Distance between two configurations in units of trajectories / cycles. Assumed to be the distance between two measurements * dtr_cnfg if not given
  • version (str): Either openQCD or sfqcd, depending on the data.
  • L (int): spatial length of the lattice in L/a. HAS to be set if version != sfqcd, since openQCD does not provide this in the header
  • r_start (list): list which contains the first config to be read for each replicum.
  • r_stop (list): list which contains the last config to be read for each replicum.
  • files (list): specify the exact files that need to be read from path, practical if e.g. only one replicum is needed
  • postfix (str): postfix of the file to read, e.g. '.gfms.dat' for openQCD-files
  • names (list): Alternative labeling for replicas/ensembles. Has to have the appropriate length.
  • Zeuthen_flow (bool): (optional) If True, the Zeuthen flow is used for Qtop. Only possible for version=='sfqcd' If False, the Wilson flow is used.
  • integer_charge (bool): If True, the charge is rounded towards the nearest integer on each config.
def read_gf_coupling(path, prefix, c, dtr_cnfg=1, Zeuthen_flow=True, **kwargs):
572def read_gf_coupling(path, prefix, c, dtr_cnfg=1, Zeuthen_flow=True, **kwargs):
573    """Read the gradient flow coupling based on sfqcd gradient flow measurements. See 1607.06423 for details.
574
575    Note: The current implementation only works for c=0.3 and T=L. The definition of the coupling in 1607.06423 requires projection to topological charge zero which is not done within this function but has to be performed in a separate step.
576
577    Parameters
578    ----------
579    path : str
580        path of the measurement files
581    prefix : str
582        prefix of the measurement files, e.g. <prefix>_id0_r0.ms.dat.
583        Ignored if file names are passed explicitly via keyword files.
584    c : double
585        Smearing radius in units of the lattice extent, c = sqrt(8 t0) / L.
586    dtr_cnfg : int
587        (optional) parameter that specifies the number of measurements
588        between two configs.
589        If it is not set, the distance between two measurements
590        in the file is assumed to be the distance between two configurations.
591    steps : int
592        (optional) Distance between two configurations in units of trajectories /
593         cycles. Assumed to be the distance between two measurements * dtr_cnfg if not given
594    r_start : list
595        list which contains the first config to be read for each replicum.
596    r_stop : list
597        list which contains the last config to be read for each replicum.
598    files : list
599        specify the exact files that need to be read
600        from path, practical if e.g. only one replicum is needed
601    names : list
602        Alternative labeling for replicas/ensembles.
603        Has to have the appropriate length.
604    postfix : str
605        postfix of the file to read, e.g. '.gfms.dat' for openQCD-files
606    Zeuthen_flow : bool
607        (optional) If True, the Zeuthen flow is used for the coupling. If False, the Wilson flow is used.
608    """
609
610    if c != 0.3:
611        raise Exception("The required lattice norm is only implemented for c=0.3 at the moment.")
612
613    plaq = _read_flow_obs(path, prefix, c, dtr_cnfg=dtr_cnfg, version="sfqcd", obspos=6, sum_t=False, Zeuthen_flow=Zeuthen_flow, integer_charge=False, **kwargs)
614    C2x1 = _read_flow_obs(path, prefix, c, dtr_cnfg=dtr_cnfg, version="sfqcd", obspos=7, sum_t=False, Zeuthen_flow=Zeuthen_flow, integer_charge=False, **kwargs)
615    L = plaq.tag["L"]
616    T = plaq.tag["T"]
617
618    if T != L:
619        raise Exception("The required lattice norm is only implemented for T=L at the moment.")
620
621    if Zeuthen_flow is not True:
622        raise Exception("The required lattice norm is only implemented for the Zeuthen flow at the moment.")
623
624    t = (c * L) ** 2 / 8
625
626    normdict = {4: 0.012341170468270,
627                6: 0.010162691462430,
628                8: 0.009031614807931,
629                10: 0.008744966371393,
630                12: 0.008650917856809,
631                14: 8.611154391267955E-03,
632                16: 0.008591758449508,
633                20: 0.008575359627103,
634                24: 0.008569387847540,
635                28: 8.566803713382559E-03,
636                32: 0.008565541650006,
637                40: 8.564480684962046E-03,
638                48: 8.564098025073460E-03,
639                64: 8.563853943383087E-03}
640
641    return t * t * (5 / 3 * plaq - 1 / 12 * C2x1) / normdict[L]

Read the gradient flow coupling based on sfqcd gradient flow measurements. See 1607.06423 for details.

Note: The current implementation only works for c=0.3 and T=L. The definition of the coupling in 1607.06423 requires projection to topological charge zero which is not done within this function but has to be performed in a separate step.

Parameters
  • path (str): path of the measurement files
  • prefix (str): prefix of the measurement files, e.g. _id0_r0.ms.dat. Ignored if file names are passed explicitly via keyword files.
  • c (double): Smearing radius in units of the lattice extent, c = sqrt(8 t0) / L.
  • dtr_cnfg (int): (optional) parameter that specifies the number of measurements between two configs. If it is not set, the distance between two measurements in the file is assumed to be the distance between two configurations.
  • steps (int): (optional) Distance between two configurations in units of trajectories / cycles. Assumed to be the distance between two measurements * dtr_cnfg if not given
  • r_start (list): list which contains the first config to be read for each replicum.
  • r_stop (list): list which contains the last config to be read for each replicum.
  • files (list): specify the exact files that need to be read from path, practical if e.g. only one replicum is needed
  • names (list): Alternative labeling for replicas/ensembles. Has to have the appropriate length.
  • postfix (str): postfix of the file to read, e.g. '.gfms.dat' for openQCD-files
  • Zeuthen_flow (bool): (optional) If True, the Zeuthen flow is used for the coupling. If False, the Wilson flow is used.
def qtop_projection(qtop, target=0):
916def qtop_projection(qtop, target=0):
917    """Returns the projection to the topological charge sector defined by target.
918
919    Parameters
920    ----------
921    path : Obs
922        Topological charge.
923    target : int
924        Specifies the topological sector to be reweighted to (default 0)
925    """
926    if qtop.reweighted:
927        raise Exception('You can not use a reweighted observable for reweighting!')
928
929    proj_qtop = []
930    for n in qtop.deltas:
931        proj_qtop.append(np.array([1 if round(qtop.value + q) == target else 0 for q in qtop.deltas[n]]))
932
933    reto = Obs(proj_qtop, qtop.names, idl=[qtop.idl[name] for name in qtop.names])
934    reto.is_merged = qtop.is_merged
935    return reto

Returns the projection to the topological charge sector defined by target.

Parameters
  • path (Obs): Topological charge.
  • target (int): Specifies the topological sector to be reweighted to (default 0)
def read_qtop_sector(path, prefix, c, target=0, **kwargs):
938def read_qtop_sector(path, prefix, c, target=0, **kwargs):
939    """Constructs reweighting factors to a specified topological sector.
940
941    Parameters
942    ----------
943    path : str
944        path of the measurement files
945    prefix : str
946        prefix of the measurement files, e.g. <prefix>_id0_r0.ms.dat
947    c : double
948        Smearing radius in units of the lattice extent, c = sqrt(8 t0) / L
949    target : int
950        Specifies the topological sector to be reweighted to (default 0)
951    dtr_cnfg : int
952        (optional) parameter that specifies the number of trajectories
953        between two configs.
954        if it is not set, the distance between two measurements
955        in the file is assumed to be the distance between two configurations.
956    steps : int
957        (optional) Distance between two configurations in units of trajectories /
958         cycles. Assumed to be the distance between two measurements * dtr_cnfg if not given
959    version : str
960        version string of the openQCD (sfqcd) version used to create
961        the ensemble. Default is 2.0. May also be set to sfqcd.
962    L : int
963        spatial length of the lattice in L/a.
964        HAS to be set if version != sfqcd, since openQCD does not provide
965        this in the header
966    r_start : list
967        offset of the first ensemble, making it easier to match
968        later on with other Obs
969    r_stop : list
970        last configurations that need to be read (per replicum)
971    files : list
972        specify the exact files that need to be read
973        from path, practical if e.g. only one replicum is needed
974    names : list
975        Alternative labeling for replicas/ensembles.
976        Has to have the appropriate length
977    Zeuthen_flow : bool
978        (optional) If True, the Zeuthen flow is used for Qtop. Only possible
979        for version=='sfqcd' If False, the Wilson flow is used.
980    """
981
982    if not isinstance(target, int):
983        raise Exception("'target' has to be an integer.")
984
985    kwargs['integer_charge'] = True
986    qtop = read_qtop(path, prefix, c, **kwargs)
987
988    return qtop_projection(qtop, target=target)

Constructs reweighting factors to a specified topological sector.

Parameters
  • path (str): path of the measurement files
  • prefix (str): prefix of the measurement files, e.g. _id0_r0.ms.dat
  • c (double): Smearing radius in units of the lattice extent, c = sqrt(8 t0) / L
  • target (int): Specifies the topological sector to be reweighted to (default 0)
  • dtr_cnfg (int): (optional) parameter that specifies the number of trajectories between two configs. if it is not set, the distance between two measurements in the file is assumed to be the distance between two configurations.
  • steps (int): (optional) Distance between two configurations in units of trajectories / cycles. Assumed to be the distance between two measurements * dtr_cnfg if not given
  • version (str): version string of the openQCD (sfqcd) version used to create the ensemble. Default is 2.0. May also be set to sfqcd.
  • L (int): spatial length of the lattice in L/a. HAS to be set if version != sfqcd, since openQCD does not provide this in the header
  • r_start (list): offset of the first ensemble, making it easier to match later on with other Obs
  • r_stop (list): last configurations that need to be read (per replicum)
  • files (list): specify the exact files that need to be read from path, practical if e.g. only one replicum is needed
  • names (list): Alternative labeling for replicas/ensembles. Has to have the appropriate length
  • Zeuthen_flow (bool): (optional) If True, the Zeuthen flow is used for Qtop. Only possible for version=='sfqcd' If False, the Wilson flow is used.