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('Error, directory 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 0 < 1:
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 0 < 1:
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    names : list
557        Alternative labeling for replicas/ensembles.
558        Has to have the appropriate length.
559    Zeuthen_flow : bool
560        (optional) If True, the Zeuthen flow is used for Qtop. Only possible
561        for version=='sfqcd' If False, the Wilson flow is used.
562    integer_charge : bool
563        If True, the charge is rounded towards the nearest integer on each config.
564    """
565    known_versions = ["openQCD", "sfqcd"]
566
567    if version not in known_versions:
568        raise Exception("Unknown openQCD version.")
569    if "steps" in kwargs:
570        steps = kwargs.get("steps")
571    if version == "sfqcd":
572        if "L" in kwargs:
573            supposed_L = kwargs.get("L")
574        else:
575            supposed_L = None
576        postfix = ".gfms.dat"
577    else:
578        if "L" not in kwargs:
579            raise Exception("This version of openQCD needs you to provide the spatial length of the lattice as parameter 'L'.")
580        else:
581            L = kwargs.get("L")
582        postfix = ".ms.dat"
583
584    if "files" in kwargs:
585        files = kwargs.get("files")
586        postfix = ''
587    else:
588        found = []
589        files = []
590        for (dirpath, dirnames, filenames) in os.walk(path + "/"):
591            found.extend(filenames)
592            break
593        for f in found:
594            if fnmatch.fnmatch(f, prefix + "*" + postfix):
595                files.append(f)
596
597    if 'r_start' in kwargs:
598        r_start = kwargs.get('r_start')
599        if len(r_start) != len(files):
600            raise Exception('r_start does not match number of replicas')
601        r_start = [o if o else None for o in r_start]
602    else:
603        r_start = [None] * len(files)
604
605    if 'r_stop' in kwargs:
606        r_stop = kwargs.get('r_stop')
607        if len(r_stop) != len(files):
608            raise Exception('r_stop does not match number of replicas')
609    else:
610        r_stop = [None] * len(files)
611    rep_names = []
612
613    zeuthen = kwargs.get('Zeuthen_flow', False)
614    if zeuthen and version not in ['sfqcd']:
615        raise Exception('Zeuthen flow can only be used for version==sfqcd')
616
617    r_start_index = []
618    r_stop_index = []
619    deltas = []
620    configlist = []
621    for rep, file in enumerate(files):
622        with open(path + "/" + file, "rb") as fp:
623
624            Q = []
625            traj_list = []
626            if version in ['sfqcd']:
627                if zeuthen:
628                    obspos = 0
629                else:
630                    obspos = 8
631                t = fp.read(12)
632                header = struct.unpack('<iii', t)
633                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)
634                ncs = header[1]  # number of different values for c in t_flow=1/8 c² L² -> measurements done for ncs c's
635                tmax = header[2]  # lattice T/a
636
637                t = fp.read(12)
638                Ls = struct.unpack('<iii', t)
639                if(Ls[0] == Ls[1] and Ls[1] == Ls[2]):
640                    L = Ls[0]
641                    if not (supposed_L == L) and supposed_L:
642                        raise Exception("It seems the length given in the header and by you contradict each other")
643                else:
644                    raise Exception("Found more than one spatial length in header!")
645
646                t = fp.read(16)
647                header2 = struct.unpack('<dd', t)
648                tol = header2[0]
649                cmax = header2[1]  # highest value of c used
650
651                if c > cmax:
652                    raise Exception('Flow has been determined between c=0 and c=%lf with tolerance %lf' % (cmax, tol))
653
654                if(zthfl == 2):
655                    nfl = 2  # number of flows
656                else:
657                    nfl = 1
658                iobs = 8 * nfl  # number of flow observables calculated
659
660                while 0 < 1:
661                    t = fp.read(4)
662                    if(len(t) < 4):
663                        break
664                    traj_list.append(struct.unpack('i', t)[0])   # trajectory number when measurement was done
665
666                    for j in range(ncs + 1):
667                        for i in range(iobs):
668                            t = fp.read(8 * tmax)
669                            if (i == obspos):  # determines the flow observable -> i=0 <-> Zeuthen flow
670                                Q.append(struct.unpack('d' * tmax, t))
671
672            else:
673                t = fp.read(12)
674                header = struct.unpack('<iii', t)
675                # step size in integration steps "dnms"
676                dn = header[0]
677                # number of measurements, so "ntot"/dn
678                nn = header[1]
679                # lattice T/a
680                tmax = header[2]
681
682                t = fp.read(8)
683                eps = struct.unpack('d', t)[0]
684
685                while 0 < 1:
686                    t = fp.read(4)
687                    if(len(t) < 4):
688                        break
689                    traj_list.append(struct.unpack('i', t)[0])
690                    # Wsl
691                    t = fp.read(8 * tmax * (nn + 1))
692                    # Ysl
693                    t = fp.read(8 * tmax * (nn + 1))
694                    # Qsl, which is asked for in this method
695                    t = fp.read(8 * tmax * (nn + 1))
696                    # unpack the array of Qtops,
697                    # on each timeslice t=0,...,tmax-1 and the
698                    # measurement number in = 0...nn (see README.qcd1)
699                    tmpd = struct.unpack('d' * tmax * (nn + 1), t)
700                    Q.append(tmpd)
701
702        if len(np.unique(np.diff(traj_list))) != 1:
703            raise Exception("Irregularities in stepsize found")
704        else:
705            if 'steps' in kwargs:
706                if steps != traj_list[1] - traj_list[0]:
707                    raise Exception("steps and the found stepsize are not the same")
708            else:
709                steps = traj_list[1] - traj_list[0]
710
711        configlist.append([tr // steps // dtr_cnfg for tr in traj_list])
712        if configlist[-1][0] > 1:
713            offset = configlist[-1][0] - 1
714            warnings.warn('Assume thermalization and that the first measurement belongs to the first config. Offset = %d configs (%d trajectories / cycles)' % (
715                offset, offset * steps))
716            configlist[-1] = [item - offset for item in configlist[-1]]
717
718        if r_start[rep] is None:
719            r_start_index.append(0)
720        else:
721            try:
722                r_start_index.append(configlist[-1].index(r_start[rep]))
723            except ValueError:
724                raise Exception('Config %d not in file with range [%d, %d]' % (
725                    r_start[rep], configlist[-1][0], configlist[-1][-1])) from None
726
727        if r_stop[rep] is None:
728            r_stop_index.append(len(configlist[-1]) - 1)
729        else:
730            try:
731                r_stop_index.append(configlist[-1].index(r_stop[rep]))
732            except ValueError:
733                raise Exception('Config %d not in file with range [%d, %d]' % (
734                    r_stop[rep], configlist[-1][0], configlist[-1][-1])) from None
735
736        if version in ['sfqcd']:
737            cstepsize = cmax / ncs
738            index_aim = round(c / cstepsize)
739        else:
740            t_aim = (c * L) ** 2 / 8
741            index_aim = round(t_aim / eps / dn)
742
743        Q_sum = []
744        for i, item in enumerate(Q):
745            Q_sum.append([sum(item[current:current + tmax])
746                         for current in range(0, len(item), tmax)])
747        Q_top = []
748        if version in ['sfqcd']:
749            for i in range(len(Q_sum) // (ncs + 1)):
750                Q_top.append(Q_sum[i * (ncs + 1) + index_aim][0])
751        else:
752            for i in range(len(Q) // dtr_cnfg):
753                Q_top.append(Q_sum[dtr_cnfg * i][index_aim])
754        if len(Q_top) != len(traj_list) // dtr_cnfg:
755            raise Exception("qtops and traj_list dont have the same length")
756
757        if kwargs.get('integer_charge', False):
758            Q_top = [round(q) for q in Q_top]
759
760        truncated_file = file[:-len(postfix)]
761
762        if "names" not in kwargs:
763            try:
764                idx = truncated_file.index('r')
765            except Exception:
766                if "names" not in kwargs:
767                    raise Exception("Automatic recognition of replicum failed, please enter the key word 'names'.")
768            ens_name = truncated_file[:idx]
769            rep_names.append(ens_name + '|' + truncated_file[idx:])
770        else:
771            names = kwargs.get("names")
772            rep_names = names
773        deltas.append(Q_top)
774
775    idl = [range(int(configlist[rep][r_start_index[rep]]), int(configlist[rep][r_stop_index[rep]]) + 1, 1) for rep in range(len(deltas))]
776    deltas = [deltas[nrep][r_start_index[nrep]:r_stop_index[nrep] + 1] for nrep in range(len(deltas))]
777    result = Obs(deltas, rep_names, idl=idl)
778    return result
779
780
781def qtop_projection(qtop, target=0):
782    """Returns the projection to the topological charge sector defined by target.
783
784    Parameters
785    ----------
786    path : Obs
787        Topological charge.
788    target : int
789        Specifies the topological sector to be reweighted to (default 0)
790    """
791    if qtop.reweighted:
792        raise Exception('You can not use a reweighted observable for reweighting!')
793
794    proj_qtop = []
795    for n in qtop.deltas:
796        proj_qtop.append(np.array([1 if round(qtop.value + q) == target else 0 for q in qtop.deltas[n]]))
797
798    reto = Obs(proj_qtop, qtop.names, idl=[qtop.idl[name] for name in qtop.names])
799    reto.is_merged = qtop.is_merged
800    return reto
801
802
803def read_qtop_sector(path, prefix, c, target=0, **kwargs):
804    """Constructs reweighting factors to a specified topological sector.
805
806    Parameters
807    ----------
808    path : str
809        path of the measurement files
810    prefix : str
811        prefix of the measurement files, e.g. <prefix>_id0_r0.ms.dat
812    c : double
813        Smearing radius in units of the lattice extent, c = sqrt(8 t0) / L
814    target : int
815        Specifies the topological sector to be reweighted to (default 0)
816    dtr_cnfg : int
817        (optional) parameter that specifies the number of trajectories
818        between two configs.
819        if it is not set, the distance between two measurements
820        in the file is assumed to be the distance between two configurations.
821    steps : int
822        (optional) Distance between two configurations in units of trajectories /
823         cycles. Assumed to be the distance between two measurements * dtr_cnfg if not given
824    version : str
825        version string of the openQCD (sfqcd) version used to create
826        the ensemble. Default is 2.0. May also be set to sfqcd.
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        offset of the first ensemble, making it easier to match
833        later on with other Obs
834    r_stop : list
835        last configurations that need to be read (per replicum)
836    files : list
837        specify the exact files that need to be read
838        from path, practical if e.g. only one replicum is needed
839    names : list
840        Alternative labeling for replicas/ensembles.
841        Has to have the appropriate length
842    Zeuthen_flow : bool
843        (optional) If True, the Zeuthen flow is used for Qtop. Only possible
844        for version=='sfqcd' If False, the Wilson flow is used.
845    """
846
847    if not isinstance(target, int):
848        raise Exception("'target' has to be an integer.")
849
850    kwargs['integer_charge'] = True
851    qtop = read_qtop(path, prefix, c, **kwargs)
852
853    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('Error, directory 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 0 < 1:
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 0 < 1:
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    names : list
558        Alternative labeling for replicas/ensembles.
559        Has to have the appropriate length.
560    Zeuthen_flow : bool
561        (optional) If True, the Zeuthen flow is used for Qtop. Only possible
562        for version=='sfqcd' If False, the Wilson flow is used.
563    integer_charge : bool
564        If True, the charge is rounded towards the nearest integer on each config.
565    """
566    known_versions = ["openQCD", "sfqcd"]
567
568    if version not in known_versions:
569        raise Exception("Unknown openQCD version.")
570    if "steps" in kwargs:
571        steps = kwargs.get("steps")
572    if version == "sfqcd":
573        if "L" in kwargs:
574            supposed_L = kwargs.get("L")
575        else:
576            supposed_L = None
577        postfix = ".gfms.dat"
578    else:
579        if "L" not in kwargs:
580            raise Exception("This version of openQCD needs you to provide the spatial length of the lattice as parameter 'L'.")
581        else:
582            L = kwargs.get("L")
583        postfix = ".ms.dat"
584
585    if "files" in kwargs:
586        files = kwargs.get("files")
587        postfix = ''
588    else:
589        found = []
590        files = []
591        for (dirpath, dirnames, filenames) in os.walk(path + "/"):
592            found.extend(filenames)
593            break
594        for f in found:
595            if fnmatch.fnmatch(f, prefix + "*" + postfix):
596                files.append(f)
597
598    if 'r_start' in kwargs:
599        r_start = kwargs.get('r_start')
600        if len(r_start) != len(files):
601            raise Exception('r_start does not match number of replicas')
602        r_start = [o if o else None for o in r_start]
603    else:
604        r_start = [None] * len(files)
605
606    if 'r_stop' in kwargs:
607        r_stop = kwargs.get('r_stop')
608        if len(r_stop) != len(files):
609            raise Exception('r_stop does not match number of replicas')
610    else:
611        r_stop = [None] * len(files)
612    rep_names = []
613
614    zeuthen = kwargs.get('Zeuthen_flow', False)
615    if zeuthen and version not in ['sfqcd']:
616        raise Exception('Zeuthen flow can only be used for version==sfqcd')
617
618    r_start_index = []
619    r_stop_index = []
620    deltas = []
621    configlist = []
622    for rep, file in enumerate(files):
623        with open(path + "/" + file, "rb") as fp:
624
625            Q = []
626            traj_list = []
627            if version in ['sfqcd']:
628                if zeuthen:
629                    obspos = 0
630                else:
631                    obspos = 8
632                t = fp.read(12)
633                header = struct.unpack('<iii', t)
634                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)
635                ncs = header[1]  # number of different values for c in t_flow=1/8 c² L² -> measurements done for ncs c's
636                tmax = header[2]  # lattice T/a
637
638                t = fp.read(12)
639                Ls = struct.unpack('<iii', t)
640                if(Ls[0] == Ls[1] and Ls[1] == Ls[2]):
641                    L = Ls[0]
642                    if not (supposed_L == L) and supposed_L:
643                        raise Exception("It seems the length given in the header and by you contradict each other")
644                else:
645                    raise Exception("Found more than one spatial length in header!")
646
647                t = fp.read(16)
648                header2 = struct.unpack('<dd', t)
649                tol = header2[0]
650                cmax = header2[1]  # highest value of c used
651
652                if c > cmax:
653                    raise Exception('Flow has been determined between c=0 and c=%lf with tolerance %lf' % (cmax, tol))
654
655                if(zthfl == 2):
656                    nfl = 2  # number of flows
657                else:
658                    nfl = 1
659                iobs = 8 * nfl  # number of flow observables calculated
660
661                while 0 < 1:
662                    t = fp.read(4)
663                    if(len(t) < 4):
664                        break
665                    traj_list.append(struct.unpack('i', t)[0])   # trajectory number when measurement was done
666
667                    for j in range(ncs + 1):
668                        for i in range(iobs):
669                            t = fp.read(8 * tmax)
670                            if (i == obspos):  # determines the flow observable -> i=0 <-> Zeuthen flow
671                                Q.append(struct.unpack('d' * tmax, t))
672
673            else:
674                t = fp.read(12)
675                header = struct.unpack('<iii', t)
676                # step size in integration steps "dnms"
677                dn = header[0]
678                # number of measurements, so "ntot"/dn
679                nn = header[1]
680                # lattice T/a
681                tmax = header[2]
682
683                t = fp.read(8)
684                eps = struct.unpack('d', t)[0]
685
686                while 0 < 1:
687                    t = fp.read(4)
688                    if(len(t) < 4):
689                        break
690                    traj_list.append(struct.unpack('i', t)[0])
691                    # Wsl
692                    t = fp.read(8 * tmax * (nn + 1))
693                    # Ysl
694                    t = fp.read(8 * tmax * (nn + 1))
695                    # Qsl, which is asked for in this method
696                    t = fp.read(8 * tmax * (nn + 1))
697                    # unpack the array of Qtops,
698                    # on each timeslice t=0,...,tmax-1 and the
699                    # measurement number in = 0...nn (see README.qcd1)
700                    tmpd = struct.unpack('d' * tmax * (nn + 1), t)
701                    Q.append(tmpd)
702
703        if len(np.unique(np.diff(traj_list))) != 1:
704            raise Exception("Irregularities in stepsize found")
705        else:
706            if 'steps' in kwargs:
707                if steps != traj_list[1] - traj_list[0]:
708                    raise Exception("steps and the found stepsize are not the same")
709            else:
710                steps = traj_list[1] - traj_list[0]
711
712        configlist.append([tr // steps // dtr_cnfg for tr in traj_list])
713        if configlist[-1][0] > 1:
714            offset = configlist[-1][0] - 1
715            warnings.warn('Assume thermalization and that the first measurement belongs to the first config. Offset = %d configs (%d trajectories / cycles)' % (
716                offset, offset * steps))
717            configlist[-1] = [item - offset for item in configlist[-1]]
718
719        if r_start[rep] is None:
720            r_start_index.append(0)
721        else:
722            try:
723                r_start_index.append(configlist[-1].index(r_start[rep]))
724            except ValueError:
725                raise Exception('Config %d not in file with range [%d, %d]' % (
726                    r_start[rep], configlist[-1][0], configlist[-1][-1])) from None
727
728        if r_stop[rep] is None:
729            r_stop_index.append(len(configlist[-1]) - 1)
730        else:
731            try:
732                r_stop_index.append(configlist[-1].index(r_stop[rep]))
733            except ValueError:
734                raise Exception('Config %d not in file with range [%d, %d]' % (
735                    r_stop[rep], configlist[-1][0], configlist[-1][-1])) from None
736
737        if version in ['sfqcd']:
738            cstepsize = cmax / ncs
739            index_aim = round(c / cstepsize)
740        else:
741            t_aim = (c * L) ** 2 / 8
742            index_aim = round(t_aim / eps / dn)
743
744        Q_sum = []
745        for i, item in enumerate(Q):
746            Q_sum.append([sum(item[current:current + tmax])
747                         for current in range(0, len(item), tmax)])
748        Q_top = []
749        if version in ['sfqcd']:
750            for i in range(len(Q_sum) // (ncs + 1)):
751                Q_top.append(Q_sum[i * (ncs + 1) + index_aim][0])
752        else:
753            for i in range(len(Q) // dtr_cnfg):
754                Q_top.append(Q_sum[dtr_cnfg * i][index_aim])
755        if len(Q_top) != len(traj_list) // dtr_cnfg:
756            raise Exception("qtops and traj_list dont have the same length")
757
758        if kwargs.get('integer_charge', False):
759            Q_top = [round(q) for q in Q_top]
760
761        truncated_file = file[:-len(postfix)]
762
763        if "names" not in kwargs:
764            try:
765                idx = truncated_file.index('r')
766            except Exception:
767                if "names" not in kwargs:
768                    raise Exception("Automatic recognition of replicum failed, please enter the key word 'names'.")
769            ens_name = truncated_file[:idx]
770            rep_names.append(ens_name + '|' + truncated_file[idx:])
771        else:
772            names = kwargs.get("names")
773            rep_names = names
774        deltas.append(Q_top)
775
776    idl = [range(int(configlist[rep][r_start_index[rep]]), int(configlist[rep][r_stop_index[rep]]) + 1, 1) for rep in range(len(deltas))]
777    deltas = [deltas[nrep][r_start_index[nrep]:r_stop_index[nrep] + 1] for nrep in range(len(deltas))]
778    result = Obs(deltas, rep_names, idl=idl)
779    return result

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
  • 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 qtop_projection(qtop, target=0)
782def qtop_projection(qtop, target=0):
783    """Returns the projection to the topological charge sector defined by target.
784
785    Parameters
786    ----------
787    path : Obs
788        Topological charge.
789    target : int
790        Specifies the topological sector to be reweighted to (default 0)
791    """
792    if qtop.reweighted:
793        raise Exception('You can not use a reweighted observable for reweighting!')
794
795    proj_qtop = []
796    for n in qtop.deltas:
797        proj_qtop.append(np.array([1 if round(qtop.value + q) == target else 0 for q in qtop.deltas[n]]))
798
799    reto = Obs(proj_qtop, qtop.names, idl=[qtop.idl[name] for name in qtop.names])
800    reto.is_merged = qtop.is_merged
801    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)
804def read_qtop_sector(path, prefix, c, target=0, **kwargs):
805    """Constructs reweighting factors to a specified topological sector.
806
807    Parameters
808    ----------
809    path : str
810        path of the measurement files
811    prefix : str
812        prefix of the measurement files, e.g. <prefix>_id0_r0.ms.dat
813    c : double
814        Smearing radius in units of the lattice extent, c = sqrt(8 t0) / L
815    target : int
816        Specifies the topological sector to be reweighted to (default 0)
817    dtr_cnfg : int
818        (optional) parameter that specifies the number of trajectories
819        between two configs.
820        if it is not set, the distance between two measurements
821        in the file is assumed to be the distance between two configurations.
822    steps : int
823        (optional) Distance between two configurations in units of trajectories /
824         cycles. Assumed to be the distance between two measurements * dtr_cnfg if not given
825    version : str
826        version string of the openQCD (sfqcd) version used to create
827        the ensemble. Default is 2.0. May also be set to sfqcd.
828    L : int
829        spatial length of the lattice in L/a.
830        HAS to be set if version != sfqcd, since openQCD does not provide
831        this in the header
832    r_start : list
833        offset of the first ensemble, making it easier to match
834        later on with other Obs
835    r_stop : list
836        last configurations that need to be read (per replicum)
837    files : list
838        specify the exact files that need to be read
839        from path, practical if e.g. only one replicum is needed
840    names : list
841        Alternative labeling for replicas/ensembles.
842        Has to have the appropriate length
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    """
847
848    if not isinstance(target, int):
849        raise Exception("'target' has to be an integer.")
850
851    kwargs['integer_charge'] = True
852    qtop = read_qtop(path, prefix, c, **kwargs)
853
854    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.