diff --git a/pyerrors/input/openQCD.py b/pyerrors/input/openQCD.py index 3c50a3d2..36bdd552 100644 --- a/pyerrors/input/openQCD.py +++ b/pyerrors/input/openQCD.py @@ -217,8 +217,7 @@ def read_rwms(path, prefix, version='2.0', names=None, **kwargs): return result -def extract_t0(path, prefix, dtr_read, xmin, - spatial_extent, fit_range=5, **kwargs): +def extract_t0(path, prefix, dtr_read, xmin, spatial_extent, fit_range=5, **kwargs): """Extract t0 from given .ms.dat files. Returns t0 as Obs. It is assumed that all boundary effects have @@ -226,7 +225,6 @@ def extract_t0(path, prefix, dtr_read, 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. - Only works with openQCD 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 @@ -510,8 +508,289 @@ def _read_array_openQCD2(fp): return {'d': d, 'n': n, 'size': size, 'arr': arr} -def read_qtop(path, prefix, c, dtr_cnfg=1, version="1.2", **kwargs): - """Read qtop format from given folder structure. +def read_qtop(path, prefix, c, dtr_cnfg=1, version="openQCD", **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 + 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. + """ + known_versions = ["openQCD", "sfqcd"] + + if version not in known_versions: + raise Exception("Unknown openQCD version.") + if "steps" in kwargs: + steps = kwargs.get("steps") + if version == "sfqcd": + if "L" in kwargs: + supposed_L = kwargs.get("L") + else: + supposed_L = None + postfix = ".gfms.dat" + else: + if "L" not in kwargs: + raise Exception("This version of openQCD needs you to provide the spatial length of the lattice as parameter 'L'.") + else: + L = kwargs.get("L") + postfix = ".ms.dat" + + if "files" in kwargs: + files = kwargs.get("files") + postfix = '' + else: + found = [] + files = [] + for (dirpath, dirnames, filenames) in os.walk(path + "/"): + # print(filenames) + found.extend(filenames) + break + for f in found: + if fnmatch.fnmatch(f, prefix + "*" + postfix): + files.append(f) + + if 'r_start' in kwargs: + r_start = kwargs.get('r_start') + if len(r_start) != len(files): + raise Exception('r_start does not match number of replicas') + r_start = [o if o else None for o in r_start] + else: + r_start = [None] * len(files) + + if 'r_stop' in kwargs: + r_stop = kwargs.get('r_stop') + if len(r_stop) != len(files): + raise Exception('r_stop does not match number of replicas') + else: + r_stop = [None] * len(files) + rep_names = [] + + zeuthen = kwargs.get('Zeuthen_flow', False) + if zeuthen and version not in ['sfqcd']: + raise Exception('Zeuthen flow can only be used for version==sfqcd') + + r_start_index = [] + r_stop_index = [] + deltas = [] + configlist = [] + for rep, file in enumerate(files): + with open(path + "/" + file, "rb") as fp: + + Q = [] + traj_list = [] + if version in ['sfqcd']: + if zeuthen: + obspos = 0 + else: + obspos = 8 + t = fp.read(12) + header = struct.unpack(' if it's equal to 2 it means that the Zeuthen flow is also 'measured' (apart from the Wilson flow) + ncs = header[1] # number of different values for c in t_flow=1/8 c² L² -> measurements done for ncs c's + tmax = header[2] # lattice T/a + + t = fp.read(12) + Ls = struct.unpack(' cmax: + raise Exception('Flow has been determined between c=0 and c=%lf with tolerance %lf' % (cmax, tol)) + + if(zthfl == 2): + nfl = 2 # number of flows + else: + nfl = 1 + iobs = 8 * nfl # number of flow observables calculated + + while 0 < 1: + t = fp.read(4) + if(len(t) < 4): + break + traj_list.append(struct.unpack('i', t)[0]) # trajectory number when measurement was done + + for j in range(ncs + 1): + for i in range(iobs): + t = fp.read(8 * tmax) + if (i == obspos): # determines the flow observable -> i=0 <-> Zeuthen flow + Q.append(struct.unpack('d' * tmax, t)) + + else: + t = fp.read(12) + header = struct.unpack(' 1: + offset = configlist[-1][0] - 1 + warnings.warn('Assume thermalization and that the first measurement belongs to the first config. Offset = %d configs (%d trajectories / cycles)' % ( + offset, offset * steps)) + configlist[-1] = [item - offset for item in configlist[-1]] + + if r_start[rep] is None: + r_start_index.append(0) + else: + try: + r_start_index.append(configlist[-1].index(r_start[rep])) + except ValueError: + raise Exception('Config %d not in file with range [%d, %d]' % ( + r_start[rep], configlist[-1][0], configlist[-1][-1])) from None + + if r_stop[rep] is None: + r_stop_index.append(len(configlist[-1]) - 1) + else: + try: + r_stop_index.append(configlist[-1].index(r_stop[rep])) + except ValueError: + raise Exception('Config %d not in file with range [%d, %d]' % ( + r_stop[rep], configlist[-1][0], configlist[-1][-1])) from None + + if version in ['sfqcd']: + cstepsize = cmax / ncs + index_aim = round(c / cstepsize) + else: + t_aim = (c * L) ** 2 / 8 + index_aim = round(t_aim / eps / dn) + + Q_sum = [] + for i, item in enumerate(Q): + Q_sum.append([sum(item[current:current + tmax]) + for current in range(0, len(item), tmax)]) + Q_top = [] + if version in ['sfqcd']: + for i in range(len(Q_sum) // (ncs + 1)): + Q_top.append(Q_sum[i * (ncs + 1) + index_aim][0]) + else: + for i in range(len(Q) // dtr_cnfg): + Q_top.append(Q_sum[dtr_cnfg * i][index_aim]) + if len(Q_top) != len(traj_list) // dtr_cnfg: + raise Exception("qtops and traj_list dont have the same length") + + if kwargs.get('integer_charge', False): + Q_top = [round(q) for q in Q_top] + + truncated_file = file[:-len(postfix)] + + if "names" not in kwargs: + try: + idx = truncated_file.index('r') + except Exception: + if "names" not in kwargs: + raise Exception("Automatic recognition of replicum failed, please enter the key word 'names'.") + ens_name = truncated_file[:idx] + rep_names.append(ens_name + '|' + truncated_file[idx:]) + else: + names = kwargs.get("names") + rep_names = names + deltas.append(Q_top) + + idl = [range(int(configlist[rep][r_start_index[rep]]), int(configlist[rep][r_stop_index[rep]]), 1) for rep in range(len(deltas))] + deltas = [deltas[nrep][r_start_index[nrep]:r_stop_index[nrep]] for nrep in range(len(deltas))] + result = Obs(deltas, rep_names, idl=idl) + return result + + +def qtop_projection(qtop, target=0): + """Returns the projection to the topological charge sector defined by target. + + Parameters + ---------- + path : Obs + Topological charge, rounded to nearest integer on each config. + target : int + Specifies the topological sector to be reweighted to (default 0) + """ + if qtop.reweighted: + raise Exception('You can not use a reweighted observable for reweighting!') + + proj_qtop = [] + for n in qtop.deltas: + proj_qtop.append(np.array([1 if int(qtop.value + q) == target else 0 for q in qtop.deltas[n]])) + + reto = Obs(proj_qtop, qtop.names, idl=[qtop.idl[name] for name in qtop.names]) + reto.is_merged = qtop.is_merged + return reto + + +def read_qtop_sector(path, prefix, c, target=0, **kwargs): + """Constructs reweighting factors to a specified topological sector. Parameters ---------- @@ -521,18 +800,19 @@ def read_qtop(path, prefix, c, dtr_cnfg=1, version="1.2", **kwargs): 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. + in the file is assumed to be the distance between two configurations. steps : int - (optional) (maybe only necessary for openQCD2.0) - nt step size, guessed if not given + (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 + 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 @@ -548,200 +828,17 @@ def read_qtop(path, prefix, c, dtr_cnfg=1, version="1.2", **kwargs): names : list Alternative labeling for replicas/ensembles. Has to have the appropriate length - """ - known_versions = ["1.0", "1.2", "1.4", "1.6", "2.0", "sfqcd"] - - if version not in known_versions: - raise Exception("Unknown openQCD version.") - if "steps" in kwargs: - steps = kwargs.get("steps") - if version == "sfqcd": - if "L" in kwargs: - supposed_L = kwargs.get("L") - else: - if "L" not in kwargs: - raise Exception("This version of openQCD needs you to provide the spatial length of the lattice as parameter 'L'.") - else: - L = kwargs.get("L") - r_start = 1 - if "r_start" in kwargs: - r_start = kwargs.get("r_start") - if "r_stop" in kwargs: - r_stop = kwargs.get("r_stop") - if "files" in kwargs: - files = kwargs.get("files") - else: - found = [] - files = [] - for (dirpath, dirnames, filenames) in os.walk(path + "/"): - # print(filenames) - found.extend(filenames) - break - for f in found: - if fnmatch.fnmatch(f, prefix + "*" + ".ms.dat"): - files.append(f) - print(files) - rep_names = [] - - deltas = [] - idl = [] - for rep, file in enumerate(files): - with open(path + "/" + file, "rb") as fp: - t = fp.read(12) - header = struct.unpack('