from __future__ import absolute_import
from __future__ import print_function
import re
import sys
import itertools
try:
from collections import OrderedDict
except ImportError:
from .oldpymodules import OrderedDict
from .modelchems import Method, BasisSet, Error, methods, bases, errors
try:
dict.iteritems
except AttributeError:
# Python 3
def itervalues(d):
return iter(d.values())
def iteritems(d):
return iter(d.items())
else:
# Python 2
[docs] def itervalues(d):
return d.itervalues()
[docs] def iteritems(d):
return d.iteritems()
mc_archive = {'mtd': methods, 'bas': bases, 'err': errors}
fancy_mc_archive = {}
for tier in [methods, bases, errors]:
for k, v in iteritems(tier):
fancy_mc_archive[k] = v.latex
# define helper functions for formatting table cells
[docs]def val(kw):
return r"""%s%s""" % (kw['matelem'], kw['footnote'])
[docs]def graphics(kw):
mc = '-'.join([kw[bit] for bit in ['mtd', 'opt', 'bas']])
return r"""\includegraphics[width=6.67cm,height=3.5mm]{%s%s.pdf}""" % (kw['plotpath'], mc)
[docs]def flat(kw):
if kw['matelem'].strip():
dbssmc = '-'.join([kw[bit] for bit in ['dbse', 'sset', 'mtd', 'opt', 'bas']])
return r"""\includegraphics[width=6.67cm,height=3.5mm]{%sflat_%s.pdf}""" % (kw['plotpath'], dbssmc)
else:
return ''
[docs]def liliowa(kw):
if kw['matelem'].strip():
dbssmc = '-'.join([kw[bit] for bit in ['dbse', 'sset', 'mtd', 'opt', 'bas']])
return r"""\includegraphics[height=3.5mm]{%sliliowa_%s.pdf}""" % (kw['plotpath'], dbssmc)
else:
return ''
[docs]def lmtdbas(kw):
return """%-25s""" % (methods[kw['mtd']].latex + '/' + bases[kw['bas']].latex)
[docs]def label(kw):
kwt = kw['target']
return """ %-25s""" % (mc_archive[kwt][kw[kwt]].latex if kwt in mc_archive else kw[kwt])
[docs]def label2(kw):
"""This and fancy_mc_archive are experimental alternative for
summoning up col/row headers. Safe so long as mtd/bas/opt have
orthogonal keys.
"""
try:
return fancy_mc_archive[kw]
except KeyError as e:
print("""Consider adding {} to modelchems.py""".format(e))
return kw
[docs]def count(kw):
return r"""%6d""" % (kw['count'])
[docs]def empty(kw):
return ''
[docs]def table_generic(dbse, serrors,
mtd, bas, columnplan, rowplan=['bas', 'mtd'],
opt=['CP'], err=['mae'], sset=['default'],
landscape=False, standalone=True, subjoin=True, suppressblanks=False,
footnotes=[], title='', indextitle='',
plotpath='', theme=''):
"""
Arrays *mtd* and *bas* contain the keys to the qcdb.Method and
qcdb.BasisSet objects that span all those that the table may
encompass. If method and basis are to be scanned over, the arrays
should be in the desired order.
"""
def table_header(kw, abbr, head1, head0, head2):
"""Form table header"""
ref = r"""tbl:qcdb-%s-%s""" % (theme, '-'.join([kw[bit] for bit in tag]))
fancy_kw = {k: (mc_archive[k][v].latex if k in mc_archive else v) for k, v in iteritems(kw)}
text.append('')
text.append(r"""\begingroup""")
text.append(r"""\squeezetable""")
text.append(r"""\begin{%s}[h!tp]""" % ('sidewaystable' if landscape else 'table'))
text.append(r"""\renewcommand{\baselinestretch}{1}""")
text.append(r"""\caption{%s""" % (title.format(**fancy_kw).replace('_', '\\_')))
text.append(r"""\label{%s}}""" % (ref))
indices.append(r"""\scriptsize \ref{%s} & \scriptsize %s \\ """ % \
(ref, indextitle.format(**fancy_kw)))
text.append(r"""\begin{ruledtabular}""")
text.append(r"""\begin{tabular}{%s}""" % (abbr))
text.append(head1)
text.append(head0)
text.append(head2)
text.append(hline)
def table_footer():
"""Form table footer"""
# search-and-replace footnotes
fnmatch = re.compile(r"""(?P<cellpre>.*)""" + r"""(\\footnotemark)\{(?P<fntext>.*)\}""" + r"""(?P<cellpost>.*)""")
otfcounter = len(footnotes) + 1
lines2replace = {}
otffootnotes = OrderedDict()
for idx, line in enumerate(text):
newcells = []
changed = False
for cell in line.split('&'):
res = fnmatch.match(cell)
if res:
if res.group('fntext') in otffootnotes:
localcounter = otffootnotes[res.group('fntext')]
else:
localcounter = otfcounter
if localcounter == 52: # fn symbols run out at "zz"
otffootnotes['Missing some reactions'] = localcounter
else:
otfcounter += 1
otffootnotes[res.group('fntext')] = localcounter
newcells.append(res.group('cellpre') + r"""\footnotemark[""" + str(localcounter) + ']' + res.group('cellpost'))
changed = True
else:
newcells.append(cell)
if changed:
lines2replace[idx] = '&'.join(newcells)
for idx, line in iteritems(lines2replace):
text[idx] = line
# search-and-suppress "blank" lines
if suppressblanks:
for idx, line in enumerate(text):
if line.strip().endswith('\\'):
innards = ''.join(line.rstrip(""" \\""").split('&')[1:])
if innards.isspace():
text[idx] = '%' + text[idx]
# finish out table
text.append(r"""\end{tabular}""")
text.append(r"""\end{ruledtabular}""")
for idx, fn in enumerate(footnotes):
text.append(r"""\footnotetext[%d]{%s}""" % (idx + 1, fn))
for fn, idx in iteritems(otffootnotes):
text.append(r"""\footnotetext[%d]{%s}""" % (idx, fn))
text.append(r"""\end{%s}""" % ('sidewaystable' if landscape else 'table'))
text.append(r"""\endgroup""")
text.append(r"""\clearpage""")
text.append('')
def matelem(dict_row, dict_col):
"""Return merge of index dictionaries *dict_row* and *dict_col* (precedence) with error string from serrors appended at key 'matelem'."""
kw = dict(dict_row, **dict_col)
errpiece = serrors['-'.join([kw[bit] for bit in ['mtd', 'opt', 'bas']])][kw['sset']][kw['dbse']]
kw['matelem'] = errpiece[kw['err']]
if 'tgtcnt' in errpiece:
kw['count'] = errpiece['tgtcnt']
if 'misscnt' in errpiece and errpiece['misscnt'] != 0 and 'tgtcnt' in errpiece:
kw['footnote'] = r"""\footnotemark{Missing %d of %d reactions.}""" % (errpiece['misscnt'], errpiece['tgtcnt'])
else:
kw['footnote'] = ''
return kw
# avoid misunderstandings
keysincolumnplan = set(sum([col[-1].keys() for col in columnplan], []))
for key in ['dbse', 'sset', 'mtd', 'opt', 'bas', 'err']:
if len(locals()[key]) > 1:
if key not in rowplan and key not in keysincolumnplan:
print("""Warning: non-first values in argument '{0}' won't """
"""get used. Add '{0}' to rowplan to iterate over """
"""the values or add to columnplan to access"""
"""different values.""".format(key))
sys.exit()
# form LaTeX reference tag
tag = []
for key in ['dbse', 'sset', 'mtd', 'opt', 'bas', 'err']:
if len(locals()[key]) == 1 or (key == rowplan[0] and not subjoin):
tag.append(key)
tag = set(tag)
for col in columnplan:
tag -= set(col[4].keys())
# form column headers
start = 1
stop = 1
head0 = ''
for index in range(2, len(columnplan)):
if columnplan[index][1] == columnplan[index - 1][1]:
stop = index
else:
head0 += r"""\cline{%d-%d}""" % (start + 1, stop + 1)
start = index
stop = index
if index + 1 == len(columnplan):
head0 += r"""\cline{%d-%d}""" % (start + 1, stop + 1)
abbr = ''.join([col[0] for col in columnplan])
h1 = [(k, len(list(g))) for k, g in itertools.groupby([col[1] for col in columnplan])]
head1 = ' & '.join([r"""\multicolumn{%d}{c}{\textbf{%s}}""" % (repeat, label) for (label, repeat) in h1]) + r""" \\ """
h2 = [(k, len(list(g))) for k, g in itertools.groupby([col[2] for col in columnplan])]
head2 = ' & '.join([r"""\multicolumn{%d}{c}{\textbf{%s}}""" % (repeat, label) for (label, repeat) in h2]) + r""" \\ """
# form table body
text = []
indices = []
nH = len(rowplan)
hline = r"""\hline"""
kw = {'plotpath': plotpath, 'sset': sset[0], 'dbse': dbse[0], 'err': err[0],
'mtd': mtd[0], 'opt': opt[0], 'bas': bas[0]}
if standalone:
text += begin_latex_document()
if nH == 1:
subjoin = True
if subjoin:
table_header(kw, abbr, head1, head0, head2)
if text[-1] != hline:
text.append(hline)
for hier0 in locals()[rowplan[0]]:
kw[rowplan[0]] = hier0
kw['target'] = rowplan[0]
if nH > 1:
if not subjoin:
table_header(kw, abbr, head1, head0, head2)
if text[-1] != hline:
text.append(hline)
#text.append(r"""\textbf{%s} \\ """ % (mc_archive[rowplan[0]][hier0].latex))
text.append(r"""\textbf{%s} \\ """ % (label2(hier0)))
for hier1 in locals()[rowplan[1]]:
kw[rowplan[1]] = hier1
kw['target'] = rowplan[1]
if nH > 2:
#text.append(r"""\enspace\textbf{%s} \\ """ % (mc_archive[rowplan[1]][hier1].latex))
text.append(r"""\enspace\textbf{%s} \\ """ % (label2(hier1)))
for hier2 in locals()[rowplan[2]]:
kw[rowplan[2]] = hier2
kw['target'] = rowplan[2]
text.append(r"""\enspace\enspace""" + ' & '.join([col[3](matelem(kw, col[4])) for col in columnplan]) + r""" \\ """)
else:
text.append(r"""\enspace""" + ' & '.join([col[3](matelem(kw, col[4])) for col in columnplan]) + r""" \\ """)
if not subjoin:
table_footer()
else:
text.append(' & '.join([col[3](matelem(kw, col[4])) for col in columnplan]) + r""" \\ """)
if subjoin:
table_footer()
if standalone:
text += end_latex_document()
return text, indices
[docs]def begin_latex_document():
"""Returns array of lines at start of LaTeX file that
enable it to be compiled as its own document (standalone).
"""
text = []
text.append('')
text.append(r"""\documentclass[aip,jcp,preprint,superscriptaddress,floatfix]{revtex4-1}""")
text.append(r"""\usepackage{bm}""")
text.append(r"""\usepackage{dcolumn}""")
text.append(r"""\usepackage{rotating}""")
text.append(r"""\usepackage{longtable}""")
text.append(r"""\usepackage[table]{xcolor}""")
text.append(r"""\begin{document}""")
text.append('')
return text
[docs]def end_latex_document():
"""Returns array of lines at end of LaTeX file that
enable it to be compiled as its own document (standalone).
"""
text = []
text.append('')
text.append(r"""\end{document}""")
return text
if __name__ == "__main__":
serrors = {'MP2-CP-adtz': {'hb': {'S22': OrderedDict([('mae', ' 0.38'), ('mape', ' 2.5')]), 'HBC1': OrderedDict([('mae', ' 0.28'), ('mape', ' 2.5')]), 'NBC1': None, 'HSG': OrderedDict([('mae', ' 0.28'), ('mape', ' 1.9')]), 'DB4': OrderedDict([('mae', ' 0.31'), ('mape', ' 2.3')])}, 'default': {'S22': OrderedDict([('mae', ' 0.89'), ('mape', ' 19.2')]), 'HBC1': OrderedDict([('mae', ' 0.28'), ('mape', ' 2.5')]), 'NBC1': OrderedDict([('mae', ' 1.10'), ('mape', ' 139.0')]), 'HSG': OrderedDict([('mae', ' 0.18'), ('mape', ' 9.6')]), 'DB4': OrderedDict([('mae', ' 0.61'), ('mape', ' 42.6')])}, 'mxdd': {'S22': OrderedDict([('mae', ' 1.13'), ('mape', ' 27.1')]), 'HBC1': None, 'NBC1': OrderedDict([('mae', ' 1.10'), ('mape', ' 139.0')]), 'HSG': OrderedDict([('mae', ' 0.16'), ('mape', ' 10.9')]), 'DB4': OrderedDict([('mae', ' 0.80'), ('mape', ' 59.0')])}}, 'CCSD-CP-adz': {'hb': {'S22': OrderedDict([('mae', ' 2.42'), ('mape', ' 18.0')]), 'HBC1': OrderedDict([('mae', ' 2.09'), ('mape', ' 15.3')]), 'NBC1': None, 'HSG': OrderedDict([('mae', ' 2.29'), ('mape', ' 14.8')]), 'DB4': OrderedDict([('mae', ' 2.27'), ('mape', ' 16.1')])}, 'default': {'S22': OrderedDict([('mae', ' 1.82'), ('mape', ' 32.3')]), 'HBC1': OrderedDict([('mae', ' 2.09'), ('mape', ' 15.3')]), 'NBC1': OrderedDict([('mae', ' 1.15'), ('mape', ' 147.4')]), 'HSG': OrderedDict([('mae', ' 1.14'), ('mape', ' 85.9')]), 'DB4': OrderedDict([('mae', ' 1.55'), ('mape', ' 70.2')])}, 'mxdd': {'S22': OrderedDict([('mae', ' 1.54'), ('mape', ' 39.0')]), 'HBC1': None, 'NBC1': OrderedDict([('mae', ' 1.15'), ('mape', ' 147.4')]), 'HSG': OrderedDict([('mae', ' 0.95'), ('mape', ' 97.7')]), 'DB4': OrderedDict([('mae', ' 1.21'), ('mape', ' 94.7')])}}, 'MP2-CP-atz': {'hb': {'S22': OrderedDict([('mae', ' 0.70'), ('mape', ' 5.0')]), 'HBC1': OrderedDict([('mae', ' 0.52'), ('mape', ' 4.1')]), 'NBC1': None, 'HSG': OrderedDict([('mae', ' 0.61'), ('mape', ' 4.0')]), 'DB4': OrderedDict([('mae', ' 0.61'), ('mape', ' 4.4')])}, 'default': {'S22': OrderedDict([('mae', ' 0.86'), ('mape', ' 17.0')]), 'HBC1': OrderedDict([('mae', ' 0.52'), ('mape', ' 4.1')]), 'NBC1': OrderedDict([('mae', ' 0.98'), ('mape', ' 126.7')]), 'HSG': OrderedDict([('mae', ' 0.24'), ('mape', ' 13.5')]), 'DB4': OrderedDict([('mae', ' 0.65'), ('mape', ' 40.3')])}, 'mxdd': {'S22': OrderedDict([('mae', ' 0.94'), ('mape', ' 22.6')]), 'HBC1': None, 'NBC1': OrderedDict([('mae', ' 0.98'), ('mape', ' 126.7')]), 'HSG': OrderedDict([('mae', ' 0.18'), ('mape', ' 15.1')]), 'DB4': OrderedDict([('mae', ' 0.70'), ('mape', ' 54.8')])}}, 'CCSD-CP-atz': {'hb': {'S22': OrderedDict([('mae', ' 1.41'), ('mape', ' 10.5')]), 'HBC1': OrderedDict([('mae', ' 1.11'), ('mape', ' 8.4')]), 'NBC1': None, 'HSG': OrderedDict([('mae', ' 1.39'), ('mape', ' 9.0')]), 'DB4': OrderedDict([('mae', ' 1.30'), ('mape', ' 9.3')])}, 'default': {'S22': OrderedDict([('mae', ' 1.27'), ('mape', ' 23.5')]), 'HBC1': OrderedDict([('mae', ' 1.11'), ('mape', ' 8.4')]), 'NBC1': OrderedDict([('mae', ' 0.97'), ('mape', ' 123.2')]), 'HSG': OrderedDict([('mae', ' 0.77'), ('mape', ' 57.9')]), 'DB4': OrderedDict([('mae', ' 1.03'), ('mape', ' 53.3')])}, 'mxdd': {'S22': OrderedDict([('mae', ' 1.20'), ('mape', ' 29.6')]), 'HBC1': None, 'NBC1': OrderedDict([('mae', ' 0.97'), ('mape', ' 123.2')]), 'HSG': OrderedDict([('mae', ' 0.67'), ('mape', ' 66.1')]), 'DB4': OrderedDict([('mae', ' 0.95'), ('mape', ' 73.0')])}}, 'CCSD-CP-adtz': {'hb': {'S22': OrderedDict([('mae', ' 1.03'), ('mape', ' 7.6')]), 'HBC1': OrderedDict([('mae', ' 0.76'), ('mape', ' 5.9')]), 'NBC1': None, 'HSG': OrderedDict([('mae', ' 1.02'), ('mape', ' 6.6')]), 'DB4': OrderedDict([('mae', ' 0.94'), ('mape', ' 6.7')])}, 'default': {'S22': OrderedDict([('mae', ' 1.05'), ('mape', ' 20.0')]), 'HBC1': OrderedDict([('mae', ' 0.76'), ('mape', ' 5.9')]), 'NBC1': OrderedDict([('mae', ' 0.90'), ('mape', ' 113.9')]), 'HSG': OrderedDict([('mae', ' 0.62'), ('mape', ' 46.7')]), 'DB4': OrderedDict([('mae', ' 0.83'), ('mape', ' 46.6')])}, 'mxdd': {'S22': OrderedDict([('mae', ' 1.06'), ('mape', ' 25.7')]), 'HBC1': None, 'NBC1': OrderedDict([('mae', ' 0.90'), ('mape', ' 113.9')]), 'HSG': OrderedDict([('mae', ' 0.55'), ('mape', ' 53.3')]), 'DB4': OrderedDict([('mae', ' 0.84'), ('mape', ' 64.3')])}}, 'MP2-CP-adz': {'hb': {'S22': OrderedDict([('mae', ' 1.64'), ('mape', ' 12.2')]), 'HBC1': OrderedDict([('mae', ' 1.41'), ('mape', ' 10.4')]), 'NBC1': None, 'HSG': OrderedDict([('mae', ' 1.42'), ('mape', ' 9.1')]), 'DB4': OrderedDict([('mae', ' 1.49'), ('mape', ' 10.6')])}, 'default': {'S22': OrderedDict([('mae', ' 0.97'), ('mape', ' 16.2')]), 'HBC1': OrderedDict([('mae', ' 1.41'), ('mape', ' 10.4')]), 'NBC1': OrderedDict([('mae', ' 0.70'), ('mape', ' 95.9')]), 'HSG': OrderedDict([('mae', ' 0.46'), ('mape', ' 30.5')]), 'DB4': OrderedDict([('mae', ' 0.88'), ('mape', ' 38.3')])}, 'mxdd': {'S22': OrderedDict([('mae', ' 0.65'), ('mape', ' 18.0')]), 'HBC1': None, 'NBC1': OrderedDict([('mae', ' 0.70'), ('mape', ' 95.9')]), 'HSG': OrderedDict([('mae', ' 0.30'), ('mape', ' 34.1')]), 'DB4': OrderedDict([('mae', ' 0.55'), ('mape', ' 49.3')])}}}
#print serrors['CCSD-CP-adtz']['hb']['HSG']['mape']
columnplan = [
['l', r"""Method \& Basis Set""", '', label, {}],
['d', r'S22', 'HB', val, {'sset': 'hb', 'dbse': 'S22'}],
['d', r'S22', 'MX/DD', val, {'sset': 'mxdd', 'dbse': 'S22'}],
['d', r'S22', 'TT', val, {'sset': 'default', 'dbse': 'S22'}],
['d', r'Overall', 'HB', val, {'sset': 'hb', 'dbse': 'DB4'}],
['d', r'Overall', 'MX/DD', val, {'sset': 'mxdd', 'dbse': 'DB4', 'err': 'mape'}],
['d', r'Overall', 'TT', val, {'sset': 'default', 'dbse': 'DB4'}]]
tbl, ind = table_generic(columnplan=columnplan, dbse=['DB4'], serrors=serrors,
mtd=['MP2', 'CCSD'], bas=['adz', 'atz'],
opt=['CP'], err=['mae', 'mape'],
theme='test', subjoin=False)
print('\n'.join(tbl))