From 0f735c0d2d2ef71dd34272624c78640a398b9706 Mon Sep 17 00:00:00 2001 From: Simon Kuberski Date: Wed, 13 Apr 2022 16:05:30 +0200 Subject: [PATCH] Some changes on dobs --- pyerrors/input/dobs.py | 301 +++++++++++++---------------------------- tests/json_io_test.py | 188 ++++++++++++------------- 2 files changed, 192 insertions(+), 297 deletions(-) diff --git a/pyerrors/input/dobs.py b/pyerrors/input/dobs.py index 05e5a879..de13f181 100644 --- a/pyerrors/input/dobs.py +++ b/pyerrors/input/dobs.py @@ -13,9 +13,9 @@ from ..covobs import Covobs from .. import version as pyerrorsversion -# standard pobs -# https://stackoverflow.com/a/10076823 +# Based on https://stackoverflow.com/a/10076823 def _etree_to_dict(t): + """ Convert the content of an XML file to a python dict""" d = {t.tag: {} if t.attrib else None} children = list(t) if children: @@ -38,164 +38,6 @@ def _etree_to_dict(t): return d -def _get_data_from_root(d, root): - tree = et.ElementTree(root) - nl = [] - pl = [] - for node in root.findall('.//array'): - nl.append(node) - pl.append(tree.getpath(node)) - - for nd in range(len(nl)): - s = pl[nd].split('/') - ad = d - for i in range(len(s) - 1): - if len(s[i]) > 0: - ad = ad[s[i]] - nr = int(ad['nr']) - ad = ad['array'] - if '[' in s[-1]: - ai = int(s[-1].lstrip('array[').rstrip(']')) - 1 - ad = ad[ai] - if nr == 1: - dats = [t for t in nl[nd].itertext()][0] - else: - dats = [t for t in nl[nd].itertext()][-1] - dats = [o.strip() for o in dats.split('\n')] - - layout = ad['layout'] - sl = layout.split() - datl = [] - idx = [] - for di in dats: - if len(di) > 0: - ds = di.split() - if sl[1] == 'i': - i = int(ds[0]) - idx.append(i) - df = np.asarray(ds[1:], dtype=np.float64) - else: - df = np.asarray(ds, dtype=np.float64) - nfloat = int(sl[-1].lstrip('f')) - assert len(df) == nfloat - datl.append(df) - ad['idx'] = idx - ad['#data'] = np.transpose(np.array(datl)) - return d - - -# https://stackoverflow.com/a/9808122 -def _find(key, value): - for k, v in (value.items() if isinstance(value, dict) else enumerate(value) if isinstance(value, list) else []): - if k == key: - yield v - elif isinstance(v, (dict, list)): - for result in _find(key, v): - yield result - - -# generate obs from all 'array' entries inside a dict -def _gen_obs_from_dict(d): - pobs_list = [a for a in _find('array', d)] - ol = [] - for po in pobs_list: - if isinstance(po, list): - names = [] - idl = [poi['idx'] for poi in po] - names = [poi['id'] for poi in po] - for i in range(len(po[0])): - tag = po[0]['symbol'].split()[-8 + i] - data = [poi['#data'][i] for poi in po] - ol.append(Obs(data, names, idl=idl)) - ol[-1].tag = tag - print(tag) - return ol - - -# generate obs from entries inside a 'pobs'-dict -def _gen_obs_from_pobs_dict(d): - po = d['array'] - if isinstance(po, dict): - po = [po] - enstag = d['enstag'] - nr = int(d['nr']) - assert len(po) == nr, 'Lenght of array %d does not match nr=%d' % (len(po), nr) - ol = [] - names = [] - idl = [poi['idx'] for poi in po] - names = [poi['id'] for poi in po] - names = [name.replace(enstag, enstag + '|') for name in names] - if len(po[0]['symbol'].split()) >= len(po[0]): - gettag = True - else: - gettag = False - nobs = np.shape(po[0]['#data'])[0] - print(nobs, 'observables') - for i in range(nobs): - if gettag: - tag = po[0]['symbol'].split()[i + 1] - else: - tag = po[0]['symbol'] - data = [poi['#data'][i] for poi in po] - ol.append(Obs(data, names, idl=idl)) - ol[-1].tag = tag - return ol - - -def _read_pobs_traditional(fname, full_output=False): - """Import a list of Obs from an xml.gz file in the Zeuthen pobs format, - this does not include pobs that are written using the dobs package. - - Parameters - ---------- - fname : str - Filename of the input file. - full_output : bool - If True, a dict containing auxiliary information and the data is returned. - If False, only the data is returned as list. - """ - # open and read gzipped xml file - infile = gzip.open(fname) - content = infile.read() - - # parse xml file content - root = et.fromstring(content) - - # parse to dict - d = _etree_to_dict(root) - - # get the actual data from the xml file - d = _get_data_from_root(d, root) - - # pobs dict: - pod = d[list(d.keys())[0]]['pobs'] - - # convert data to Obs - ol = _gen_obs_from_pobs_dict(pod) - - if full_output: - retd = {} - retd['obsdata'] = ol - for k in pod.keys(): - if k == 'spec' and isinstance(pod[k], dict): - retd[k] = '' - if '#data' in pod[k]: - if isinstance(pod[k]['#data'], list): - for el in pod[k]['#data']: - retd[k] += el - else: - retd[k] = pod[k]['#data'] - for li in pod[k]: - if not li == '#data': - retd[k] += '\n<%s>\n ' % (li) + pod[k][li] + '\n<%s%s>' % ('/', li) - elif not k == 'array': - retd[k] = pod[k] - - return retd - else: - return ol - - def _dict_to_xmlstring(d): if isinstance(d, dict): iters = '' @@ -243,7 +85,7 @@ def _dict_to_xmlstring_spaces(d, space=' '): return o -def create_pobs_string(obsl, name, spec='', origin='', symbol=[]): +def create_pobs_string(obsl, name, spec='', origin='', symbol=[], enstag=None): """Export a list of Obs or structures containing Obs to an xml string according to the Zeuthen pobs format. @@ -262,6 +104,8 @@ def create_pobs_string(obsl, name, spec='', origin='', symbol=[]): Specify where the data has its origin. symbol : list A list of symbols that describe the observables to be written. May be empty. + enstag : str + Enstag that is written to pobs. If None, the ensemble name is used. """ od = {} @@ -288,7 +132,12 @@ def create_pobs_string(obsl, name, spec='', origin='', symbol=[]): pd['spec'] = spec pd['origin'] = origin pd['name'] = name - pd['enstag'] = ename + if enstag: + if not isinstance(enstag, str): + raise Exception('enstag has to be a string!') + pd['enstag'] = enstag + else: + pd['enstag'] = ename pd['nr'] = '%d' % (nr) pd['array'] = [] osymbol = 'cfg' @@ -322,7 +171,7 @@ def create_pobs_string(obsl, name, spec='', origin='', symbol=[]): return rs -def write_pobs(obsl, fname, name, spec='', origin='', symbol=[], gz=True): +def write_pobs(obsl, fname, name, spec='', origin='', symbol=[], enstag=None, gz=True): """Export a list of Obs or structures containing Obs to a .xml.gz file according to the Zeuthen pobs format. @@ -343,10 +192,12 @@ def write_pobs(obsl, fname, name, spec='', origin='', symbol=[], gz=True): Specify where the data has its origin. symbol : list A list of symbols that describe the observables to be written. May be empty. + enstag : str + Enstag that is written to pobs. If None, the ensemble name is used. gz : bool If True, the output is a gzipped json. If False, the output is a json file. """ - pobsstring = create_pobs_string(obsl, name, spec, origin, symbol) + pobsstring = create_pobs_string(obsl, name, spec, origin, symbol, enstag) if not fname.endswith('.xml') and not fname.endswith('.gz'): fname += '.xml' @@ -372,11 +223,18 @@ def _check(condition): raise Exception("XML file format not supported") +class _NoTagInDataError(Exception): + """Raised when tag is not in data""" + def __init__(self, tag): + self.tag = tag + super().__init__('Tag %s not in data!' % (self.tag)) + + def _find_tag(dat, tag): for i in range(len(dat)): if dat[i].tag == tag: return i - raise Exception('Tag %s not in data!' % (tag)) + raise _NoTagInDataError(tag) def _import_array(arr): @@ -384,7 +242,7 @@ def _import_array(arr): index = _find_tag(arr, 'layout') try: sindex = _find_tag(arr, 'symbol') - except: + except _NoTagInDataError: sindex = 0 if sindex > index: tmp = _import_data(arr[sindex].tail) @@ -430,7 +288,7 @@ def _import_cdata(cd): return cd[0].text.strip(), cov, grad -def read_pobs(fname, full_output=False, gz=True): +def read_pobs(fname, full_output=False, gz=True, separator_insertion=None): """Import a list of Obs from an xml.gz file in the Zeuthen pobs format. Tags are not written or recovered automatically. @@ -442,6 +300,10 @@ def read_pobs(fname, full_output=False, gz=True): full_output : bool If True, a dict containing auxiliary information and the data is returned. If False, only the data is returned as list. + separatior_insertion: str or int + str: replace all occurences of "separator_insertion" within the replica names + by "|%s" % (separator_insertion) when constructing the names of the replica. + int: Insert the separator "|" at position separator_insertion """ if not fname.endswith('.xml') and not fname.endswith('.gz'): @@ -463,32 +325,28 @@ def read_pobs(fname, full_output=False, gz=True): _check(root[2].tag == 'pobs') pobs = root[2] - if root.tag == 'observables' and False: - _check(root[0].tag in ['schema']) - version = root[0][1].text.strip() + version = root[0][1].text.strip() - _check(root[1].tag == 'origin') - file_origin = _etree_to_dict(root[1])['origin'] + _check(root[1].tag == 'origin') + file_origin = _etree_to_dict(root[1])['origin'] - res = _read_pobs_traditional(fname) - - else: - # _check(root.tag in ['OBSERVABLES']) - # _check(root[0].tag in ['SCHEMA']) - version = root[0][1].text.strip() - - _check(root[1].tag == 'origin') - file_origin = _etree_to_dict(root[1])['origin'] - - deltas = [] - names = [] - idl = [] - for i in range(5, len(pobs)): - delta, name, idx = _import_rdata(pobs[i]) - deltas.append(delta) - names.append(name) - idl.append(idx) - res = [Obs([d[i] for d in deltas], names, idl=idl) for i in range(len(deltas[0]))] + deltas = [] + names = [] + idl = [] + for i in range(5, len(pobs)): + delta, name, idx = _import_rdata(pobs[i]) + deltas.append(delta) + if separator_insertion is None: + pass + elif isinstance(separator_insertion, int): + name = name[:separator_insertion] + '|' + name[separator_insertion:] + elif isinstance(separator_insertion, str): + name = name.replace(separator_insertion, "|%s" % (separator_insertion)) + else: + raise Exception("separator_insertion has to be string or int, is ", type(separator_insertion)) + names.append(name) + idl.append(idx) + res = [Obs([d[i] for d in deltas], names, idl=idl) for i in range(len(deltas[0]))] descriptiond = {} for i in range(4): @@ -520,9 +378,13 @@ def read_pobs(fname, full_output=False, gz=True): return res +# Reading (and writing) dobs is not yet working properly: +# we have to loop over root[2:] because each entry is a dobs +# But maybe this is just a problem with Ben's implementation + # this is based on Mattia Bruno's implementation at https://github.com/mbruno46/pyobs/blob/master/pyobs/IO/xml.py -def import_dobs_string(content, noempty=False, full_output=False): - """Import a list of Obs from an xml.gz file in the Zeuthen dobs format. +def import_dobs_string(content, noempty=False, full_output=False, separator_insertion=None): + """Import a list of Obs from a string in the Zeuthen dobs format. Tags are not written or recovered automatically. @@ -536,6 +398,10 @@ def import_dobs_string(content, noempty=False, full_output=False): full_output : bool If True, a dict containing auxiliary information and the data is returned. If False, only the data is returned as list. + separatior_insertion: str or int + str: replace all occurences of "separator_insertion" within the replica names + by "|%s" % (separator_insertion) when constructing the names of the replica. + int: Insert the separator "|" at position separator_insertion """ root = et.fromstring(content) @@ -574,6 +440,7 @@ def import_dobs_string(content, noempty=False, full_output=False): gradd = {} names = [] e_names = [] + enstags = {} for k in range(6, len(list(dobs))): if dobs[k].tag == "edata": _check(dobs[k][0].tag == "enstag") @@ -583,8 +450,20 @@ def import_dobs_string(content, noempty=False, full_output=False): R = int(dobs[k][1].text.strip()) for i in range(2, 2 + R): deltas, rname, idx = _import_rdata(dobs[k][i]) - if rname != ename: - rname = rname[:len(ename)] + '|' + rname[len(ename):] + if separator_insertion is None: + if rname.startswith(ename): + rname = rname[:len(ename)] + '|' + rname[len(ename):] + elif isinstance(separator_insertion, int): + rname = rname[:separator_insertion] + '|' + rname[separator_insertion:] + elif isinstance(separator_insertion, str): + rname = rname.replace(separator_insertion, "|%s" % (separator_insertion)) + else: + raise Exception("separator_insertion has to be string or int, is ", type(separator_insertion)) + if '|' in rname: + new_ename = rname[:rname.index('|')] + else: + new_ename = ename + enstags[new_ename] = ename idld[rname] = idx deltad[rname] = deltas names.append(rname) @@ -602,6 +481,7 @@ def import_dobs_string(content, noempty=False, full_output=False): for name in names: for i in range(len(deltad[name])): deltad[name][i] = np.array(deltad[name][i]) + mean[i] + res = [] for i in range(len(mean)): deltas = [] @@ -615,7 +495,7 @@ def import_dobs_string(content, noempty=False, full_output=False): obs_names.append(name) idl.append(idld[name]) res.append(Obs(deltas, obs_names, idl=idl)) - + print(mean, 'vs', res) _check(len(e_names) == ne) cnames = list(covd.keys()) @@ -654,13 +534,14 @@ def import_dobs_string(content, noempty=False, full_output=False): retd['date'] = file_origin['date'] retd['host'] = file_origin['host'] retd['description'] = descriptiond + retd['enstags'] = enstags retd['obsdata'] = res return retd else: return res -def read_dobs(fname, noempty=False, full_output=False, gz=True): +def read_dobs(fname, noempty=False, full_output=False, gz=True, separator_insertion=None): """Import a list of Obs from an xml.gz file in the Zeuthen dobs format. Tags are not written or recovered automatically. @@ -677,6 +558,10 @@ def read_dobs(fname, noempty=False, full_output=False, gz=True): If False, only the data is returned as list. gz : bool If True, assumes that data is gzipped. If False, assumes XML file. + separatior_insertion: str or int + str: replace all occurences of "separator_insertion" within the replica names + by "|%s" % (separator_insertion) when constructing the names of the replica. + int: Insert the separator "|" at position separator_insertion """ if not fname.endswith('.xml') and not fname.endswith('.gz'): @@ -696,7 +581,7 @@ def read_dobs(fname, noempty=False, full_output=False, gz=True): infile = gzip.open(fname) content = infile.read() - return import_dobs_string(content, noempty, full_output) + return import_dobs_string(content, noempty, full_output, separator_insertion=separator_insertion) def _dobsdict_to_xmlstring(d): @@ -758,7 +643,7 @@ def _dobsdict_to_xmlstring_spaces(d, space=' '): return o -def create_dobs_string(obsl, name, spec='dobs v1.0', origin='', symbol=[], who=None): +def create_dobs_string(obsl, name, spec='dobs v1.0', origin='', symbol=[], who=None, enstags={}): """Generate the string for the export of a list of Obs or structures containing Obs to a .xml.gz file according to the Zeuthen dobs format. @@ -780,6 +665,9 @@ def create_dobs_string(obsl, name, spec='dobs v1.0', origin='', symbol=[], who=N A list of symbols that describe the observables to be written. May be empty. who : str Provide the name of the person that exports the data. + enstags : dict + Provide alternative enstag for ensembles in the form enstags = {ename: enstag} + Otherwise, the ensemble name is used. """ od = {} r_names = [] @@ -787,6 +675,9 @@ def create_dobs_string(obsl, name, spec='dobs v1.0', origin='', symbol=[], who=N r_names += [name for name in o.names if name.split('|')[0] in o.mc_names] r_names = sorted(set(r_names)) mc_names = sorted(set([n.split('|')[0] for n in r_names])) + for tmpname in mc_names: + if tmpname not in enstags: + enstags[tmpname] = tmpname ne = len(set(mc_names)) cov_names = [] for o in obsl: @@ -827,7 +718,7 @@ def create_dobs_string(obsl, name, spec='dobs v1.0', origin='', symbol=[], who=N pd['edata'] = [] for name in mc_names: ed = {} - ed['enstag'] = name + ed['enstag'] = enstags[name] onames = sorted([n for n in r_names if (n.startswith(name + '|') or n == name)]) nr = len(onames) ed['nr'] = nr @@ -852,7 +743,6 @@ def create_dobs_string(obsl, name, spec='dobs v1.0', origin='', symbol=[], who=N data += '0 ' continue if o.idl[repname][counters[oi]] == ci: - # c = o.idl[repname][counters[oi]] num = o.deltas[repname][counters[oi]] if num == 0: data += '0 ' @@ -926,7 +816,7 @@ def create_dobs_string(obsl, name, spec='dobs v1.0', origin='', symbol=[], who=N return rs -def write_dobs(obsl, fname, name, spec='dobs v1.0', origin='', symbol=[], who=None, gz=True): +def write_dobs(obsl, fname, name, spec='dobs v1.0', origin='', symbol=[], who=None, enstags={}, gz=True): """Export a list of Obs or structures containing Obs to a .xml.gz file according to the Zeuthen dobs format. @@ -950,11 +840,14 @@ def write_dobs(obsl, fname, name, spec='dobs v1.0', origin='', symbol=[], who=No A list of symbols that describe the observables to be written. May be empty. who : str Provide the name of the person that exports the data. + enstags : dict + Provide alternative enstag for ensembles in the form enstags = {ename: enstag} + Otherwise, the ensemble name is used. gz : bool - If True, the output is a gzipped json. If False, the output is a json file. + If True, the output is a gzipped XML. If False, the output is a XML file. """ - dobsstring = create_dobs_string(obsl, name, spec, origin, symbol, who) + dobsstring = create_dobs_string(obsl, name, spec, origin, symbol, who, enstags=enstags) if not fname.endswith('.xml') and not fname.endswith('.gz'): fname += '.xml' diff --git a/tests/json_io_test.py b/tests/json_io_test.py index c82a17e9..46cd920e 100644 --- a/tests/json_io_test.py +++ b/tests/json_io_test.py @@ -6,6 +6,7 @@ import pyerrors.input.json as jsonio import pyerrors.input.dobs as dobsio import pytest + def test_jsonio(): o = pe.pseudo_Obs(1.0, .2, 'one') o2 = pe.pseudo_Obs(0.5, .1, 'two|r1') @@ -139,99 +140,6 @@ def test_json_corr_2d_io(): assert my_corr.prange == recover.prange -def test_dobsio(): - o = pe.pseudo_Obs(1.0, .2, 'one') - o1 = pe.pseudo_Obs(1.5, .2, 'one') - - ol = [o, o1] - fname = 'test_rw' - dobsio.write_pobs(ol, fname, 'Testobs') - rl = dobsio.read_pobs(fname) - - for o, r in zip(ol, rl): - assert np.all(o == r) - - od = { - 'obsdata': ol, - 'name': 'testn', - 'spec': 'tests', - 'origin': 'testo', - 'symbol': ['A', 'B'] - } - dobsio.write_pobs(ol, fname, od['name'], od['spec'], od['origin'], od['symbol']) - rd = dobsio.read_pobs(fname, full_output=True) - - for o, r in zip(od['obsdata'], rd['obsdata']): - assert np.all(o == r) - assert(od['spec'] == rd['description']['spec']) - assert(od['origin'] == rd['description']['origin']) - assert(od['name'] == rd['description']['name']) - assert(rd['description']['enstag'] == ol[0].e_names[0]) - - dobsio.write_dobs(ol, fname, 'Testobs') - rl = dobsio.read_dobs(fname) - - for o, r in zip(ol, rl): - assert np.all(o == r) - - dobsio.write_dobs(ol, fname, od['name'], od['spec'], od['origin'], od['symbol']) - rd = dobsio.read_dobs(fname, full_output=True) - - for o, r in zip(od['obsdata'], rd['obsdata']): - assert np.all(o == r) - assert(od['spec'] == rd['description']['spec']) - assert(od['origin'] == rd['description']['origin']) - assert(od['name'] == rd['description']['name']) - - o2 = pe.pseudo_Obs(0.5, .1, 'two|r1') - o3 = pe.pseudo_Obs(0.5, .1, 'two|r2') - o4 = pe.merge_obs([o2, o3]) - otag = 'This has been merged!' - o4.tag = otag - do = o - .2 * o4 - co1 = pe.cov_Obs(1., .123, 'cov1') - co3 = pe.cov_Obs(4., .1 ** 2, 'cov3') - do *= co1 / co3 - do.tag = {'A': 2} - - o5 = pe.pseudo_Obs(0.8, .1, 'two|r2') - co2 = pe.cov_Obs([1, 2], [[.12, .004], [.004, .02]], 'cov2') - o5 /= co2[0] - #o3 /= co2[1] - o5.tag = 2 * otag - - tt1 = pe.Obs([np.random.rand(100)], ['t|r1'], idl=[range(2, 202, 2)]) - tt2 = pe.Obs([np.random.rand(100)], ['t|r2'], idl=[range(2, 202, 2)]) - tt3 = pe.Obs([np.random.rand(102)], ['qe']) - - tt = tt1 + tt2 + tt3 - - tt.tag = 'Test Obs: Ä' - - ol = [o2, o3, o4, do, o5, tt] - print(ol) - fname = 'test_rw' - - dobsio.write_dobs(ol, fname, 'TEST') - - rl = dobsio.read_dobs(fname, noempty=True) - [o.gamma_method() for o in rl] - - #os.remove(fname + '.xml.gz') - - for o, r in zip(ol, rl): - assert np.all(o == r) - - for i in range(len(ol)): - if isinstance(ol[i], pe.Obs): - o = ol[i] - rl[i] - assert(o.is_zero()) - or1 = np.ravel(ol[i]) - or2 = np.ravel(rl[i]) - for j in range(len(or1)): - o = or1[j] - or2[j] - assert(o.is_zero()) - def test_json_dict_io(): def check_dict_equality(d1, d2): def dict_check_obs(d1, d2): @@ -344,3 +252,97 @@ def test_renorm_deriv_of_corr(tmp_path): pe.input.json.dump_to_json(c, (tmp_path / 'test').as_posix()) recover = pe.input.json.load_json((tmp_path / 'test').as_posix()) assert np.all([o == 0 for o in (c - recover)[1:-1]]) + + +def test_dobsio(): + o = pe.pseudo_Obs(1.0, .2, 'one') + o1 = pe.pseudo_Obs(1.5, .2, 'one') + + ol = [o, o1] + fname = 'test_rw' + dobsio.write_pobs(ol, fname, 'Testobs') + rl = dobsio.read_pobs(fname) + + for o, r in zip(ol, rl): + assert np.all(o == r) + + od = { + 'obsdata': ol, + 'name': 'testn', + 'spec': 'tests', + 'origin': 'testo', + 'symbol': ['A', 'B'] + } + dobsio.write_pobs(ol, fname, od['name'], od['spec'], od['origin'], od['symbol']) + rd = dobsio.read_pobs(fname, full_output=True) + + for o, r in zip(od['obsdata'], rd['obsdata']): + assert np.all(o == r) + assert(od['spec'] == rd['description']['spec']) + assert(od['origin'] == rd['description']['origin']) + assert(od['name'] == rd['description']['name']) + assert(rd['description']['enstag'] == ol[0].e_names[0]) + + dobsio.write_dobs(ol, fname, 'Testobs') + rl = dobsio.read_dobs(fname) + + for o, r in zip(ol, rl): + assert np.all(o == r) + + dobsio.write_dobs(ol, fname, od['name'], od['spec'], od['origin'], od['symbol']) + rd = dobsio.read_dobs(fname, full_output=True) + + for o, r in zip(od['obsdata'], rd['obsdata']): + assert np.all(o == r) + assert(od['spec'] == rd['description']['spec']) + assert(od['origin'] == rd['description']['origin']) + assert(od['name'] == rd['description']['name']) + + o2 = pe.pseudo_Obs(0.5, .1, 'two|r1') + o3 = pe.pseudo_Obs(0.5, .1, 'two|r2') + o4 = pe.merge_obs([o2, o3]) + otag = 'This has been merged!' + o4.tag = otag + do = o - .2 * o4 + co1 = pe.cov_Obs(1., .123, 'cov1') + co3 = pe.cov_Obs(4., .1 ** 2, 'cov3') + do *= co1 / co3 + do.tag = {'A': 2} + + o5 = pe.pseudo_Obs(0.8, .1, 'two|r2') + co2 = pe.cov_Obs([1, 2], [[.12, .004], [.004, .02]], 'cov2') + o5 /= co2[0] + #o3 /= co2[1] + o5.tag = 2 * otag + + tt1 = pe.Obs([np.random.rand(100)], ['t|r1'], idl=[range(2, 202, 2)]) + tt2 = pe.Obs([np.random.rand(100)], ['t|r2'], idl=[range(2, 202, 2)]) + tt3 = pe.Obs([np.random.rand(102)], ['qe']) + + tt = tt1 + tt2 + tt3 + + tt.tag = 'Test Obs: Ä' + + ol = [o2, o3, o4, do, o5, tt] + print(ol) + fname = 'test_rw' + + dobsio.write_dobs(ol, fname, 'TEST') + + rl = dobsio.read_dobs(fname, noempty=True) + [o.gamma_method() for o in rl] + + #os.remove(fname + '.xml.gz') + + for o, r in zip(ol, rl): + assert np.all(o == r) + + for i in range(len(ol)): + if isinstance(ol[i], pe.Obs): + o = ol[i] - rl[i] + assert(o.is_zero()) + or1 = np.ravel(ol[i]) + or2 = np.ravel(rl[i]) + for j in range(len(or1)): + o = or1[j] - or2[j] + assert(o.is_zero())