# -*- coding: utf-8 -*-
# Copyright (c) 2023, PyRETIS Development Team.
# Distributed under the LGPLv2.1+ License. See LICENSE for more info.
"""Method for generating reports from path sampling simulations.
The reports are useful for displaying results from the analysis.
Important methods defined here
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
generate_report_tis (:py:func:`.generate_report_tis`)
Generate a report for the overall results from a TIS simulation.
generate_report_tis_path (:py:func:`.generate_report_tis_path`)
Generate a report for a single TIS simulation.
generate_report_retis (:py:func:`.generate_report_retis`)
Generate a report for the overall results from a RETIS simulation.
"""
import logging
from pyretis.inout.report.markup import (generate_rst_table,
generate_latex_table)
from pyretis.inout.formats.formatter import apply_format, format_number
logger = logging.getLogger(__name__) # pylint: disable=invalid-name
logger.addHandler(logging.NullHandler())
__all__ = ['generate_report_tis', 'generate_report_tis_path',
'generate_report_retis']
[docs]def generate_report_tis_path(analysis, output='rst'):
"""Generate a report for a single TIS simulation.
Parameters
----------
analysis : dict
This is the output from the analysis.
output : string, optional
This is the desired output format. It must match one of the
formats defined in
:py:const:`pyretis.inout.report.report._TEMPLATES`. Default
is 'rst' (reStructuredText).
Returns
-------
out[0] : string
The generated report in the desired format.
out[1] : string
The file extension (i.e. file type) for the generated report.
"""
result = analysis['pathensemble']
report = {'ensemble': result['out']['ensemble'],
'figures': _get_path_figures(result['figures']),
'tables': {'interfaces': None,
'probability': None,
'path': None,
'efficiency': None}}
# Create tables
results = [result]
tables = report['tables']
tables['interfaces'] = _table_interface(results, fmt=output)[1]
tables['probability'] = _table_probability(results, fmt=output)[1]
tables['efficiency'] = _table_efficiencies(results, fmt=output)[1]
return report
def generate_report_pptis_path(analysis, output='rst'):
"""Generate a report for a single TIS simulation.
Parameters
----------
analysis : dict
This is the output from the analysis.
output : string, optional
This is the desired output format. It must match one of the
formats defined in
:py:const:`pyretis.inout.report.report._TEMPLATES`. Default
is 'rst' (reStructuredText).
Returns
-------
out[0] : string
The generated report in the desired format.
out[1] : string
The file extension (i.e. file type) for the generated report.
"""
result = analysis['pathensemble_repptis']
report = {'ensemble': result['out']['ensemble'],
'figures': _get_path_figures(result['figures']),
'tables': {'interfaces': None,
'probability': None,
'path': None,
'efficiency': None}}
# Create tables
results = [result]
tables = report['tables']
tables['interfaces'] = _table_interface(results, fmt=output)[1]
tables['probability'] = _table_probability_repptis(results, fmt=output)[1]
tables['efficiency'] = _table_efficiencies(results, fmt=output)[1]
return report
[docs]def generate_report_tis(analysis, output='rst'):
"""Generate a report for the over-all results from a TIS simulation.
Parameters
----------
analysis : dict
This is the output from the analysis.
output : string, optional
This is the desired output format. It must match one of the
formats defined in
:py:const:`pyretis.inout.report.report._TEMPLATES`. Default
is 'rst' (reStructuredText).
Returns
-------
out[0] : string
The generated report in the desired format.
out[1] : string
The file extension (i.e. file type) for the generated report.
"""
report = {'figures': {'tis': [],
'matched': None},
'tables': {'interfaces': None,
'probability': None,
'path': None,
'efficiency': None},
'numbers': {'pcross': None, 'perr': None}}
results = analysis['pathensemble']
figures = report['figures']
# Add figures:
for result in results:
figures['tis'].append(_get_path_figures(result['figures']))
# Get matched result:
matched_fig = analysis['matched']['figures']
matched_out = analysis['matched']['out']
figures['matched'] = matched_fig.get('matched-probability', None)
figures['total'] = matched_fig.get('total-probability', None)
figures['progress'] = matched_fig.get('overall-prun', None)
figures['error'] = matched_fig.get('overall-err', None)
# Get numbers:
report['numbers']['pcross'] = format_number(matched_out['prob'], 0.1, 1)
report['numbers']['perr'] = format_number(matched_out['relerror'] * 100,
0.1, 100)
# Get tables:
tables = report['tables']
tables['interfaces'] = _table_interface(results, fmt=output)[1]
tables['probability'] = _table_probability(results, fmt=output)[1]
tables['efficiency'] = _table_efficiencies(results, fmt=output)[1]
return report
[docs]def generate_report_retis(analysis, output='rst'):
"""Generate a report for the over-all results from a RETIS simulation.
Parameters
----------
analysis : dict
This is the output from the analysis.
output : string, optional
This is the desired output format. It must match one of the
formats defined in
:py:const:`pyretis.inout.report.report._TEMPLATES`. Default
is 'rst' (reStructuredText).
Returns
-------
out[0] : string
The generated report in the desired format.
out[1] : string
The file extension (i.e. file type) for the generated report.
"""
report = {'figures': {'tis': [],
'matched': None},
'tables': {'interfaces': None,
'interfaces0': None,
'path0': None,
'probability': None,
'path': None,
'efficiency': None,
'summary': None},
'numbers': {'pcross': None, 'perr': None, 'flux': None,
'flux-err': None},
'text': {'flux-unit': None}}
result0 = analysis['pathensemble0']
results = [result0] + analysis['pathensemble']
figures = report['figures']
permeability = result0['out'].get('permeability', False)
# Add figures:
for result in results[1:]:
figures['tis'].append(_get_path_figures(result['figures']))
figures['path0'] = _get_path_figures(result0['figures'])
# Get matched result:
matched_fig = analysis['matched']['figures']
matched_out = analysis['matched']['out']
figures['matched'] = matched_fig.get('matched-probability', None)
figures['total'] = matched_fig.get('total-probability', None)
figures['progress'] = matched_fig.get('overall-rrun', None)
figures['error'] = matched_fig.get('overall-err', None)
if permeability:
figures['xi'] = analysis['xi']['fig']['xirun']
figures['xierror'] = analysis['xi']['fig']['xierror']
figures['tau'] = analysis['tau']['fig']['taurun']
figures['tauerror'] = analysis['tau']['fig']['tauerror']
figures['tauref'] = analysis['tau']['fig']['tauref']
# Get numbers:
numbers = report['numbers']
numbers['pcross'] = format_number(matched_out['prob'], 0.1, 1)
numbers['perr'] = format_number(matched_out['relerror'] * 100, 0.1, 100)
numbers['flux'] = format_number(analysis['flux']['value'], 0.1, 100)
numbers['flux-err'] = format_number(analysis['flux']['error'] * 100,
0.1, 100)
numbers['rate'] = format_number(analysis['rate']['value'], 0.1, 100)
numbers['rate-err'] = format_number(analysis['rate']['error'] * 100,
0.1, 100)
numbers['permeability'] = permeability
if permeability:
numbers['xi'] = format_number(analysis['xi']['value'], 0.1, 100)
numbers['xi-err'] = format_number(analysis['xi']['error'] * 100,
0.1, 100)
numbers['tau'] = format_number(analysis['tau']['value'], 0.1, 100)
numbers['tau-err'] = format_number(analysis['tau']['error'] * 100,
0.1, 100)
numbers['perm'] = format_number(analysis['perm']['value'], 0.1, 100)
numbers['perm-err'] = format_number(analysis['perm']['error'] * 100,
0.1, 100)
report['text']['flux-unit'] = analysis['flux']['unit']
# Get tables:
tables = report['tables']
tables['interfaces'] = _table_interface(results, fmt=output)[1]
tables['probability'] = _table_probability(results[1:], fmt=output)[1]
tables['efficiency'] = _table_efficiencies(results, fmt=output)[1]
tables['summary'] = _table_summary(report, fmt=output)[1]
return report
def generate_report_retis0(analysis, output='txt'):
"""Generate a report for [0^-] in a RETIS/REPTIS simulation.
This method is primarily intended for displaying results on the
screen during the analysis.
Parameters
----------
analysis : dict
This is the output of the analysis.
output : string, optional
This is the desired output format. It must match one of the
formats defined in
:py:const:`pyretis.inout.report.report._TEMPLATES`. Default
is 'rst' (reStructuredText).
Returns
-------
out[0] : string
The generated report in the desired format.
out[1] : string
The file extension (i.e. file type) for the generated report.
"""
if output != 'txt':
output = 'txt'
logger.warning('Report for [0^-] is only indended as "txt"'
'\nOutput format "%s" ignored', output)
if 'pathensemble' in analysis:
result = analysis['pathensemble']
else:
result = analysis['pathensemble_repptis']
flux = result['out']['fluxlength']
report = {
'ensemble': result['out']['ensemble'],
'numbers': {'fluxlength': format_number(flux[0], 0, 10000),
'fluxlengtherror': format_number(flux[1] * 100, 0, 100)},
'tables': {'interfaces0': _table_interface0([result], fmt=output)[1]},
}
return report
def generate_report_repptis(analysis, output='rst'):
"""Generate a report for the over-all results from a PPRETIS simulation.
Parameters
----------
analysis : dict
This is the output from the analysis.
output : string, optional
This is the desired output format. It must match one of the
formats defined in
:py:const:`pyretis.inout.report.report._TEMPLATES`. Default
is 'rst' (reStructuredText).
Returns
-------
out[0] : string
The generated report in the desired format.
out[1] : string
The file extension (i.e. file type) for the generated report.
"""
report = {'figures': {'tis': [],
'matched': None},
'tables': {'interfaces': None,
'probability': None,
'path': None,
'efficiency': None,
'summary': None},
'numbers': {'pcross': None, 'perr': None, 'flux': None,
'flux-err': None},
'text': {'flux-unit': None}}
result0 = analysis['pathensemble0']
results = [result0] + analysis['pathensemble']
figures = report['figures']
permeability = result0['out'].get('permeability', False)
# Add figures:
for result in results[1:]:
figures['tis'].append(_get_path_figures(result['figures']))
figures['path0'] = _get_path_figures(result0['figures'])
# Get matched result:
matched_fig = analysis['matched_fig']
figures['matched'] = matched_fig.get('matched-probability', None)
figures['progress'] = matched_fig.get('overall-prun', None)
figures['error'] = matched_fig.get('overall-err', None)
if permeability:
figures['xi'] = analysis['xi']['fig']['xirun']
figures['xierror'] = analysis['xi']['fig']['xierror']
figures['tau'] = analysis['tau']['fig']['taurun']
figures['tauerror'] = analysis['tau']['fig']['tauerror']
figures['tauref'] = analysis['tau']['fig']['tauref']
# Get numbers:
numbers = report['numbers']
numbers['pcross'] = format_number(analysis['cross']['value'], 0.1, 1)
numbers['perr'] = format_number(analysis['cross']['error'] * 100,
0.1, 100)
numbers['flux'] = format_number(analysis['flux']['value'], 0.1, 100)
numbers['flux-err'] = format_number(analysis['flux']['error'] * 100,
0.1, 100)
numbers['rate'] = format_number(analysis['rate']['value'], 0.1, 100)
numbers['rate-err'] = format_number(analysis['rate']['error'] * 100,
0.1, 100)
numbers['permeability'] = permeability
if permeability:
numbers['xi'] = format_number(analysis['xi']['value'], 0.1, 100)
numbers['xi-err'] = format_number(analysis['xi']['error'] * 100,
0.1, 100)
numbers['tau'] = format_number(analysis['tau']['value'], 0.1, 100)
numbers['tau-err'] = format_number(analysis['tau']['error'] * 100,
0.1, 100)
numbers['perm'] = format_number(analysis['perm']['value'], 0.1, 100)
numbers['perm-err'] = format_number(analysis['perm']['error'] * 100,
0.1, 100)
report['text']['flux-unit'] = 'time-unit' # matched['flux']['unit']
# Get tables:
tables = report['tables']
tables['interfaces'] = _table_interface_repptis(results, fmt=output)[1]
tables['probability'] = _table_probability_repptis(results[1:],
fmt=output)[1]
tables['efficiency'] = _table_efficiencies(results, fmt=output)[1]
tables['summary'] = _table_summary(report, fmt=output)[1]
return report
[docs]def _table_interface_repptis(results, fmt='rst'):
"""Generate the table for the interfaces.
This table will display the location of the different interfaces.
Parameters
----------
results : list of dicts
These are the results of the analysis.
fmt : string, optional
Determines if we create reStructuredText ('rst') or
latex ('tex').
Returns
-------
out[0] : list of strings
These are the rows of the table.
out[1] : string
This is a string in the desired format which represents
the table.
"""
table = []
for idx, result in enumerate(results):
interf = result['out']['interfaces']
# To set the L interface correctly for PPRETIS
if idx > 1:
interf0 = results[idx-1]['out']['interfaces'][1]
else:
interf0 = interf[0]
detect = result['out']['detect']
row = [
'{:^8s}'.format(result['out']['ensemble']),
apply_format(interf0, '{:^8.4f}'),
apply_format(interf[1], '{:^8.4f}'),
]
intf2 = interf[2] if detect is None else detect
row.append(apply_format(intf2, '{:^8.4f}'))
table.append(row)
if fmt in ['tex', 'latex']:
table_str = generate_latex_table(table, 'Interfaces',
['Ensemble', 'Left', 'Middle',
'Detect'],
fixnum={0, 1, 2, 3})
else:
table_str = generate_rst_table(table, 'Interfaces',
['Ensemble', 'Left', 'Middle',
'Detect'])
table_txt = '\n'.join(table_str)
return table, table_txt
[docs]def _table_interface(results, fmt='rst'):
"""Generate the table for the interfaces.
This table will display the location of the different interfaces.
Parameters
----------
results : list of dicts
These are the results of the analysis.
fmt : string, optional
Determines if we create reStructuredText ('rst') or
latex ('tex').
Returns
-------
out[0] : list of strings
These are the rows of the table.
out[1] : string
This is a string in the desired format which represents
the table.
"""
table = []
for result in results:
interf = result['out']['interfaces']
row = [
'{:^8s}'.format(result['out']['ensemble']),
apply_format(interf[0], '{:^8.4f}'),
apply_format(interf[1], '{:^8.4f}'),
]
detect = result['out']['detect']
if detect is None:
row.append('')
else:
row.append(apply_format(detect, '{:^8.4f}'))
table.append(row)
if fmt in ['tex', 'latex']:
table_str = generate_latex_table(table, 'Interfaces',
['Ensemble', 'Left', 'Middle',
'Detect'],
fixnum={0, 1, 2, 3})
else:
table_str = generate_rst_table(table, 'Interfaces',
['Ensemble', 'Left', 'Middle',
'Detect'])
table_txt = '\n'.join(table_str)
return table, table_txt
[docs]def _table_interface0(results, fmt='rst'):
"""Generate the table for the [0^-] ensemble.
This table will display the location of the different interfaces.
Parameters
----------
results : list of dicts
These are the results of the analysis.
fmt : string, optional
Determines if we create reStructuredText ('rst') or
latex ('tex').
Returns
-------
out[0] : list of strings
These are the rows of the table.
out[1] : string
This is a string in the desired format which represents
the table.
"""
table = []
for result in results:
row = ['{:^8s}'.format(result['out']['ensemble'])]
interf = result['out']['interfaces']
row.append(apply_format(interf[0], '{:^8.4f}'))
row.append(apply_format(interf[1], '{:^8.4f}'))
row.append(apply_format(interf[2], '{:^8.4f}'))
table.append(row)
if fmt in ['tex', 'latex']:
table_str = generate_latex_table(table, 'Interfaces',
['Ensemble', 'Left', 'Middle',
'Right'],
fixnum={0, 1, 2, 3})
else:
table_str = generate_rst_table(table, 'Interfaces',
['Ensemble', 'Left', 'Middle',
'Right'])
table_txt = '\n'.join(table_str)
return table, table_txt
[docs]def _table_probability(results, fmt='rst'):
"""Generate the table for the probabilities.
This table will display the crossing probabilities with
uncertainties.
Parameters
----------
results : list of dicts
The dictionaries are the results obtained from the analysis.
fmt : string, optional
Determines if we create reStructuredText ('rst') or
latex ('tex').
Returns
-------
out[0] : list of strings
These are the rows of the table.
out[1] : string
This is a string in reStructuredText format which represents
the table.
"""
table = []
for result in results:
row = [
'{:^8s}'.format(result['out']['ensemble']),
apply_format(result['out']['prun'][-1], '{:^10.6f}'),
apply_format(result['out']['blockerror'][2], '{:^10.6f}'),
apply_format(result['out']['blockerror'][4] * 100, '{:^10.6f}'),
]
table.append(row)
if fmt in ['tex', 'latex']:
table_str = generate_latex_table(table, r'Crossing probabilities',
[r'Ensemble', r'$P_\text{cross}$',
r'Error', r'Rel. error (\%)'],
fixnum={0, 1, 2, 3})
else:
table_str = generate_rst_table(table, r'Crossing probabilities',
[r'Ensemble', r'Pcross', r'Error',
r'Rel. error (%)'])
table_txt = '\n'.join(table_str)
return table, table_txt
[docs]def _table_probability_repptis(results, fmt='rst'):
"""Generate the table for the probabilities.
This table will display the crossing probabilities with
uncertainties.
Parameters
----------
results : list of dicts
The dictionaries are the results obtained from the analysis.
fmt : string, optional
Determines if we create reStructuredText ('rst') or
latex ('tex').
Returns
-------
out[0] : list of strings
These are the rows of the table.
out[1] : string
This is a string in reStructuredText format which represents
the table.
"""
table = []
for result in results:
row = [
'{:^8s}'.format(result['out']['ensemble']),
apply_format(result['out']['prun_sl'][-1], '{:^10.6f}'),
apply_format(result['out']['prun_sr'][-1], '{:^10.6f}'),
apply_format(result['out']['blockerror_sl'][2], '{:^10.6f}'),
apply_format(result['out']['blockerror_sr'][2], '{:^10.6f}'),
]
table.append(row)
if fmt in ['tex', 'latex']:
str1 = r'$\left\langle p^{ \pm}\right\rangle_{\text {full }}$'
str2 = r'$\left\langle p^{ \mp}\right\rangle_{\text {full }}$'
table_str = generate_latex_table(table, r'Probabilities',
[r'Ensemble', str1, str2,
r'Error$^{ \pm}$',
r'Error$^{ \mp}$'],
fixnum={0, 1, 2, 3, 4})
else:
table_str = generate_rst_table(table, r'Probabilities',
[r'Ensemble', r'Pcross pm',
r'Pcross mp', r'Error',
r'Rel. error (%)'])
table_txt = '\n'.join(table_str)
return table, table_txt
[docs]def _table_efficiencies(results, fmt='rst'):
"""Generate table for efficiencies.
This table will display results for the efficiencies, acceptance
ratios and correlation.
Parameters
----------
results : list of dicts
The dictionaries are the results obtained from the analysis.
fmt : string, optional
Determines if we create reStructuredText ('rst') or
latex ('tex').
Returns
-------
out[0] : list of strings
These are the rows of the table.
out[1] : string
This is a string in reStructuredText format which represents
the table.
"""
table = []
for result in results:
hist1 = result['out']['pathlength'][0]
row = [
'{:^8s}'.format(result['out']['ensemble']),
apply_format(result['out']['tis-cycles'], '{:^10.0f}'),
apply_format(result['out']['efficiency'][0], '{:^10.6f}'),
apply_format(result['out']['efficiency'][1], '{:^10.6f}'),
]
row.append(apply_format(hist1[2][0], '{:^10.6f}'))
table.append(row)
if fmt in ['tex', 'latex']:
table_str = generate_latex_table(table, 'Pathensemble data',
['Ensemble', 'TIS cycles',
'Shoot acc. ratio',
'Swap acc. ratio',
'Avg. path length'],
fixnum={0, 1, 2, 3, 4, 5})
else:
table_str = generate_rst_table(table, 'Pathensemble data',
['Ensemble', 'TIS cycles',
'Shoot acc. ratio',
'Swap acc. ratio',
'Avg. path length'])
table_txt = '\n'.join(table_str)
return table, table_txt
[docs]def _table_summary(report, fmt='rst'):
"""Generate table with summary of main results.
This table will display results for the crossing probability,
the initial flux and rate constant.
Parameters
----------
report : dict
Dict with current report. We use the information in this
dictionary to generate the table.
fmt : string, optional
Determines if we create reStructuredText ('rst') or
latex ('tex').
Returns
-------
out[0] : list of strings
These are the rows of the table.
out[1] : string
This is a string in reStructuredText format which represents
the table.
"""
numbers = report['numbers']
text = report['text']
table = [['Crossing probability', numbers['pcross'], numbers['perr']],
['Flux (1/{})'.format(text['flux-unit']), numbers['flux'],
numbers['flux-err']],
['Rate constant (1/{})'.format(text['flux-unit']),
numbers['rate'], numbers['rate-err']]]
if fmt in ['tex', 'latex']:
table_str = generate_latex_table(table, 'Summary of main results',
['Property', 'Value',
r'Relative error ($\%$)'],
fixnum={1, 2})
else:
table_str = generate_rst_table(table, 'Summary of main results',
['Property', 'Value',
'Relative error (%)'])
table_txt = '\n'.join(table_str)
return table, table_txt