diff --git a/biosimulators_utils/report/data_model.py b/biosimulators_utils/report/data_model.py index 4607d7a8..77606e42 100644 --- a/biosimulators_utils/report/data_model.py +++ b/biosimulators_utils/report/data_model.py @@ -9,7 +9,7 @@ import enum -__all__ = ['DataGeneratorResults', 'VariableResults', 'ReportResults', 'ReportFormat'] +__all__ = ['VariableResults', 'DataGeneratorResults', 'DataSetResults', 'ReportResults', 'ReportFormat'] class VariableResults(dict): @@ -38,21 +38,24 @@ class DataGeneratorResults(dict): pass -class ReportResults(dict): - """ Dictionary that maps the ids of reports (e.g., :obj:`Report`) to their results (:obj:`pandas.DataFrame`) +class DataSetResults(dict): + """ Dictionary that maps the ids of data sets to their results (:obj:`numpy.ndarray`) - * Keys (:obj:`str`): ids of reports (e.g., :obj:`Report`) - * Values (:obj:`pandas.DataFrame`): result of each reports + * Keys (:obj:`str`): ids of data sets + * Values (:obj:`numpy.ndarray`): result of each data set - * Data: + * Steady-state tasks of non-spatial models: results should be arrays of shape ``(number of data sets, 1)`` + * One-step tasks of non-spatial models: results should be arrays of shape ``(number of data sets, 2)`` + * Uniform time course tasks of non-spatial models: results should be arrays of shape ``(number_of_points + 1)`` + """ + pass - * Steady-state tasks of non-spatial models: results should be arrays of shape ``(number of data sets, 1)`` - * One-step tasks of non-spatial models: results should be arrays of shape ``(number of data sets, 2)`` - * Uniform time course tasks of non-spatial models: results should be arrays of shape ``(number of data sets, number_of_points + 1)`` - * Indices (row labels) +class ReportResults(dict): + """ Dictionary that maps the ids of reports (e.g., :obj:`Report`) to their results (:obj:`DataSetResults`) - * Reports: equal to the ids of the data sets if each report + * Keys (:obj:`str`): ids of reports (e.g., :obj:`Report`) + * Values (:obj:`DataSetResults`): result of each report """ pass diff --git a/biosimulators_utils/report/io.py b/biosimulators_utils/report/io.py index 9ebdb621..fe80b28f 100644 --- a/biosimulators_utils/report/io.py +++ b/biosimulators_utils/report/io.py @@ -7,8 +7,13 @@ """ from ..config import get_config -from .data_model import ReportFormat +from ..sedml.data_model import Report # noqa: F401 +from ..sedml.utils import pad_arrays_to_consistent_shapes +from ..sedml.warnings import RepeatDataSetLabelsWarning +from ..warnings import warn +from .data_model import DataSetResults, ReportFormat import glob +import numpy import os import pandas import tables @@ -23,11 +28,12 @@ class ReportWriter(object): """ Class for writing reports of simulation results """ - def run(self, results, base_path, rel_path, format=ReportFormat.h5): + def run(self, report, results, base_path, rel_path, format=ReportFormat.h5): """ Save a report Args: - results (:obj:`pandas.DataFrame`): report results + report (:obj:`Report`): report + results (:obj:`DataSetResults`): results of the data sets base_path (:obj:`str`): path to save results * CSV: parent directory to save results @@ -40,14 +46,27 @@ def run(self, results, base_path, rel_path, format=ReportFormat.h5): format (:obj:`ReportFormat`, optional): report format """ + data_set_labels = [data_set.label for data_set in report.data_sets] + if len(set(data_set_labels)) < len(report.data_sets): + warn('To facilitate machine interpretation, data sets should have unique ids.', + RepeatDataSetLabelsWarning) + + results_list = [] + data_set_labels = [] + for data_set in report.data_sets: + if data_set.id in results: + results_list.append(results[data_set.id]) + data_set_labels.append(data_set.label) + results_list = pad_arrays_to_consistent_shapes(results_list) + results_df = pandas.DataFrame(numpy.array(results_list), index=data_set_labels) if format == ReportFormat.csv: filename = os.path.join(base_path, rel_path + '.' + format.value) out_dir = os.path.dirname(filename) if not os.path.isdir(out_dir): os.makedirs(out_dir) - results.to_csv(filename, - header=False) + results_df.to_csv(filename, + header=False) elif format == ReportFormat.h5: filename = os.path.join(base_path, get_config().H5_REPORTS_PATH) @@ -56,14 +75,14 @@ def run(self, results, base_path, rel_path, format=ReportFormat.h5): with warnings.catch_warnings(): warnings.simplefilter("ignore", tables.NaturalNameWarning) - results.to_hdf(filename, - key=rel_path, - format='table', - complevel=9, - complib='zlib', - mode='a', - append=False, - ) + results_df.to_hdf(filename, + key=rel_path, + format='table', + complevel=9, + complib='zlib', + mode='a', + append=False, + ) else: raise NotImplementedError('Report format {} is not supported'.format(format)) @@ -72,10 +91,11 @@ def run(self, results, base_path, rel_path, format=ReportFormat.h5): class ReportReader(object): """ Class for reading reports of simulation results """ - def run(self, base_path, rel_path, format=ReportFormat.h5): + def run(self, report, base_path, rel_path, format=ReportFormat.h5): """ Read a report for a file Args: + report (:obj:`Report`): report base_path (:obj:`str`): path to save results * CSV: parent directory to save results @@ -89,7 +109,7 @@ def run(self, base_path, rel_path, format=ReportFormat.h5): format (:obj:`ReportFormat`, optional): report format Returns: - :obj:`pandas.DataFrame`: report results + :obj:`DataSetResults`: report results """ if format == ReportFormat.csv: filename = os.path.join(base_path, rel_path + '.' + format.value) @@ -97,17 +117,46 @@ def run(self, base_path, rel_path, format=ReportFormat.h5): index_col=0, header=None) df.columns = pandas.RangeIndex(start=0, stop=df.shape[1], step=1) - return df elif format == ReportFormat.h5: filename = os.path.join(base_path, get_config().H5_REPORTS_PATH) - return pandas.read_hdf(filename, - key=rel_path, - ) + df = pandas.read_hdf(filename, + key=rel_path, + ) else: raise NotImplementedError('Report format {} is not supported'.format(format)) + results = DataSetResults() + + data_set_labels = [data_set.label for data_set in report.data_sets] + unreadable_data_sets = [] + if df.index.tolist() == data_set_labels: + for data_set in report.data_sets: + results[data_set.id] = df.loc[data_set.label, :] + + else: + data_set_label_to_index = {} + for i_data_set, data_set_label in enumerate(df.index): + if data_set_label not in data_set_label_to_index: + data_set_label_to_index[data_set_label] = i_data_set + else: + data_set_label_to_index[data_set_label] = None + + for data_set in report.data_sets: + i_data_set = data_set_label_to_index.get(data_set.label, None) + if i_data_set is None: + # results[data_set.id] = None + unreadable_data_sets.append(data_set.id) + else: + results[data_set.id] = df.loc[data_set.label, :] + + if unreadable_data_sets: + warn('Some data sets could not be read because their labels are not unique:\n - {}'.format( + '\n'.join('`' + id + '`' for id in sorted(unreadable_data_sets))), RepeatDataSetLabelsWarning) + + return results + def get_ids(self, base_path, format=ReportFormat.h5): """ Get the ids of the reports in a file diff --git a/biosimulators_utils/sedml/exec.py b/biosimulators_utils/sedml/exec.py index f2d2bf77..9cd20976 100644 --- a/biosimulators_utils/sedml/exec.py +++ b/biosimulators_utils/sedml/exec.py @@ -11,21 +11,18 @@ from ..log.utils import init_sed_document_log from ..plot.data_model import PlotFormat from ..plot.io import write_plot_2d, write_plot_3d -from ..report.data_model import VariableResults, ReportResults, ReportFormat +from ..report.data_model import VariableResults, DataSetResults, ReportResults, ReportFormat # noqa: F401 from ..report.io import ReportWriter from ..warnings import warn from .data_model import SedDocument, Task, Report, Plot2D, Plot3D from .exceptions import SedmlExecutionError -from .warnings import RepeatDataSetLabelsWarning from .io import SedmlSimulationReader from .utils import resolve_model_and_apply_xml_changes, get_variables_for_task, calc_data_generators_results from .warnings import NoTasksWarning, NoOutputsWarning import capturer import copy import datetime -import numpy import os -import pandas import sys import termcolor import types # noqa: F401 @@ -307,7 +304,7 @@ def exec_report(report, variable_results, base_out_path, rel_out_path, formats, Returns: :obj:`tuple`: - * :obj:`pandas.DataFrame`: report + * :obj:`DataSetResults`: report * :obj:`Status`: status * :obj:`Exception`: exception for failure * :obj:`bool`: whether :obj:`task` contribute a variable to the report @@ -318,21 +315,18 @@ def exec_report(report, variable_results, base_out_path, rel_out_path, formats, data_generators.add(data_set.data_generator) data_gen_results, data_gen_statuses, data_gen_exceptions, task_contributes_to_report = calc_data_generators_results( - data_generators, variable_results, report, task) + data_generators, variable_results, report, task, make_shapes_consistent=False) # collect data sets - dataset_labels = [] - dataset_results = [] + data_set_results = {} running = False succeeded = True failed = False for data_set in report.data_sets: - dataset_labels.append(data_set.label) - data_gen_res = data_gen_results[data_set.data_generator.id] - dataset_results.append(data_gen_res) + data_set_results[data_set.id] = data_gen_res data_gen_status = data_gen_statuses[data_set.data_generator.id] log.data_sets[data_set.id] = data_gen_status @@ -343,13 +337,9 @@ def exec_report(report, variable_results, base_out_path, rel_out_path, formats, else: succeeded = False - if len(set(dataset_labels)) < len(dataset_labels): - warn('To facilitate machine interpretation, data sets should have unique ids.', - RepeatDataSetLabelsWarning) - - output_df = pandas.DataFrame(numpy.array(dataset_results), index=dataset_labels) for format in formats: - ReportWriter().run(output_df, + ReportWriter().run(report, + data_set_results, base_out_path, os.path.join(rel_out_path, report.id) if rel_out_path else report.id, format=format) @@ -366,7 +356,7 @@ def exec_report(report, variable_results, base_out_path, rel_out_path, formats, else: status = Status.QUEUED - return output_df, status, data_gen_exceptions, task_contributes_to_report + return data_set_results, status, data_gen_exceptions, task_contributes_to_report def exec_plot_2d(plot, variable_results, base_out_path, rel_out_path, formats, task, log): @@ -375,25 +365,19 @@ def exec_plot_2d(plot, variable_results, base_out_path, rel_out_path, formats, t Args: plot (:obj:`Plot2D`): plot variable_results (:obj:`VariableResults`): result of each data generator - base_out_path (:obj:`str`): path to store the outputs - - * CSV: directory in which to save outputs to files - ``{base_out_path}/{rel_out_path}/{report.id}.csv`` - * HDF5: directory in which to save a single HDF5 file (``{base_out_path}/reports.h5``), - with reports at keys ``{rel_out_path}/{report.id}`` within the HDF5 file - - rel_out_path (:obj:`str`, optional): path relative to :obj:`base_out_path` to store the outputs + base_out_path (:obj:`str`): base path to store the plot. Complete path is + ``{base_out_path}/{rel_out_path}/{plot.id}.csv`` + rel_out_path (:obj:`str`, optional): path relative to :obj:`base_out_path` to store the plot formats (:obj:`list` of :obj:`PlotFormat`, optional): plot format (e.g., pdf) task (:obj:`Task`): task - log (:obj:`ReportLog`, optional): log of report + log (:obj:`ReportLog`, optional): log of plot Returns: :obj:`tuple`: - * :obj:`pandas.DataFrame`: results of data generators * :obj:`Status`: status * :obj:`Exception`: exception for failure - * :obj:`bool`: whether :obj:`task` contribute a variable to the report + * :obj:`bool`: whether :obj:`task` contributes a variable to the plot """ # calculate data generators data_generators = set() @@ -456,30 +440,24 @@ def exec_plot_2d(plot, variable_results, base_out_path, rel_out_path, formats, t def exec_plot_3d(plot, variable_results, base_out_path, rel_out_path, formats, task, log): - """ Execute a 3D plot, generating the curves which are available + """ Execute a 3D plot, generating the surfaces which are available Args: plot (:obj:`Plot3D`): plot variable_results (:obj:`VariableResults`): result of each data generator - base_out_path (:obj:`str`): path to store the outputs - - * CSV: directory in which to save outputs to files - ``{base_out_path}/{rel_out_path}/{report.id}.csv`` - * HDF5: directory in which to save a single HDF5 file (``{base_out_path}/reports.h5``), - with reports at keys ``{rel_out_path}/{report.id}`` within the HDF5 file - - rel_out_path (:obj:`str`, optional): path relative to :obj:`base_out_path` to store the outputs + base_out_path (:obj:`str`): base path to store the plot. Complete path is + ``{base_out_path}/{rel_out_path}/{plot.id}.pdf`` + rel_out_path (:obj:`str`, optional): path relative to :obj:`base_out_path` to store the plot formats (:obj:`list` of :obj:`PlotFormat`, optional): plot format (e.g., pdf) task (:obj:`Task`): task - log (:obj:`ReportLog`, optional): log of report + log (:obj:`ReportLog`, optional): log of plot Returns: :obj:`tuple`: - * :obj:`pandas.DataFrame`: results of data generators * :obj:`Status`: status * :obj:`Exception`: exception for failure - * :obj:`bool`: whether :obj:`task` contribute a variable to the report + * :obj:`bool`: whether :obj:`task` contributes a variable to the plot """ # calculate data generators data_generators = set() diff --git a/biosimulators_utils/sedml/io.py b/biosimulators_utils/sedml/io.py index b5736ce2..b289a768 100644 --- a/biosimulators_utils/sedml/io.py +++ b/biosimulators_utils/sedml/io.py @@ -1076,7 +1076,7 @@ def _read_variables(self, obj_sed, id_to_model_map, id_to_task_map): var.symbol = var_sed.getSymbol() or None var.target = var_sed.getTarget() or None - if var.target.startswith('#'): + if var.target and var.target.startswith('#'): raise NotImplementedError('Variable targets to data descriptions are not supported.') self._deserialize_reference(var_sed, var, 'task', 'Task', 'task', id_to_task_map) diff --git a/biosimulators_utils/sedml/utils.py b/biosimulators_utils/sedml/utils.py index 841b8e61..a0dd0f0b 100644 --- a/biosimulators_utils/sedml/utils.py +++ b/biosimulators_utils/sedml/utils.py @@ -38,6 +38,7 @@ 'calc_compute_model_change_new_value', 'calc_data_generator_results', 'calc_data_generators_results', + 'pad_arrays_to_consistent_shapes', 'compile_math', 'eval_math', 'remove_model_changes', @@ -644,40 +645,9 @@ def calc_data_generators_results(data_generators, variable_results, output, task results[data_gen.id] = result if make_shapes_consistent: - shapes = set() - for result in results.values(): - if result is not None: - shape = result.shape - if not shape and result.size: - shape = (1,) - shapes.add(shape) - - if len(shapes) > 1: - warn('Data generators for ouput {} do not have consistent shapes'.format(output.id), UserWarning) - - max_shape = [] - for shape in shapes: - max_shape = max_shape + [1 if max_shape else 0] * (len(shape) - len(max_shape)) - shape = list(shape) + [1 if shape else 0] * (len(max_shape) - len(shape)) - max_shape = [max(x, y) for x, y in zip(max_shape, shape)] - - for data_gen_id, result in results.items(): - if result is None: - result = numpy.full(max_shape, numpy.nan) - - shape = tuple(list(result.shape) - + [1 if result.size else 0] - * (len(max_shape) - result.ndim)) - result = result.reshape(shape) - - pad_width = tuple((0, x - y) for x, y in zip(max_shape, shape)) - - if pad_width: - result = numpy.pad(result, - pad_width, - mode='constant', - constant_values=numpy.nan) - + arrays = results.values() + consistent_arrays = pad_arrays_to_consistent_shapes(arrays) + for data_gen_id, result in zip(results.keys(), consistent_arrays): results[data_gen_id] = result if exceptions: @@ -689,6 +659,55 @@ def calc_data_generators_results(data_generators, variable_results, output, task return results, statuses, exception, task_contributes_to_data_generators +def pad_arrays_to_consistent_shapes(arrays): + """ Pad a list of NumPy arrays to a consistent shape + + Args: + arrays (:obj:`list` of :obj:`numpy.ndarray`): list of NumPy arrays + + Returns: + :obj:`list` of :obj:`numpy.ndarray`: list of padded arrays + """ + shapes = set() + for array in arrays: + if array is not None: + shape = array.shape + if not shape and array.size: + shape = (1,) + shapes.add(shape) + + if len(shapes) > 1: + warn('Arrays do not have consistent shapes', UserWarning) + + max_shape = [] + for shape in shapes: + max_shape = max_shape + [1 if max_shape else 0] * (len(shape) - len(max_shape)) + shape = list(shape) + [1 if shape else 0] * (len(max_shape) - len(shape)) + max_shape = [max(x, y) for x, y in zip(max_shape, shape)] + + padded_arrays = [] + for array in arrays: + if array is None: + array = numpy.full(max_shape, numpy.nan) + + shape = tuple(list(array.shape) + + [1 if array.size else 0] + * (len(max_shape) - array.ndim)) + array = array.reshape(shape) + + pad_width = tuple((0, x - y) for x, y in zip(max_shape, shape)) + + if pad_width: + array = numpy.pad(array, + pad_width, + mode='constant', + constant_values=numpy.nan) + + padded_arrays.append(array) + + return padded_arrays + + def compile_math(math): """ Compile a mathematical expression diff --git a/biosimulators_utils/simulator/cli.py b/biosimulators_utils/simulator/cli.py index 6dfa442f..43251f94 100644 --- a/biosimulators_utils/simulator/cli.py +++ b/biosimulators_utils/simulator/cli.py @@ -26,7 +26,7 @@ def build_cli(cli_name=None, cli_version=None, The command-line application will have two inputs * A path to a COMBINE/OMEX archive that describes one or more simulations or one or more models - * A path to a directory to store the ouputs of the execution of the simulations defined in the archive + * A path to a directory to store the outputs of the execution of the simulations defined in the archive The command-line application will also support two additional commands diff --git a/tests/report/test_report_io.py b/tests/report/test_report_io.py index bc1200a5..1a24f762 100644 --- a/tests/report/test_report_io.py +++ b/tests/report/test_report_io.py @@ -1,5 +1,6 @@ from biosimulators_utils.report import data_model from biosimulators_utils.report import io +from biosimulators_utils.sedml.data_model import Report, DataSet import numpy import os import pandas @@ -17,24 +18,33 @@ def tearDown(self): def test_write_errors(self): with self.assertRaisesRegex(NotImplementedError, 'is not supported'): - io.ReportWriter().run(None, None, None, format='TSV') + io.ReportWriter().run(Report(), None, None, None, format='TSV') def test_read_errors(self): with self.assertRaisesRegex(NotImplementedError, 'is not supported'): - io.ReportReader().run(None, None, format='TSV') + io.ReportReader().run(Report(), None, None, format='TSV') def test_get_ids(self): - data = numpy.array([[1, 2, 3], [4, 5, 6]]) - df = pandas.DataFrame(data, index=['A', 'B']) + report = Report( + data_sets=[ + DataSet(id='A', label='A'), + DataSet(id='B', label='A'), + ], + ) + + results = data_model.DataSetResults({ + report.data_sets[0].id: numpy.array([1, 2, 3]), + report.data_sets[1].id: numpy.array([4, 5, 6]), + }) for format in [data_model.ReportFormat.h5, data_model.ReportFormat.csv]: filename = os.path.join(self.dirname, 'test') - io.ReportWriter().run(df, filename, 'a/b/c.sedml/report1', format=format) - io.ReportWriter().run(df, filename, 'a/b/c.sedml/report2', format=format) - io.ReportWriter().run(df, filename, 'a/b/c.sedml/report3', format=format) - io.ReportWriter().run(df, filename, 'a/b/d.sedml/report4', format=format) - io.ReportWriter().run(df, filename, 'a/b/report5', format=format) - io.ReportWriter().run(df, filename, 'a/b/report6', format=format) + io.ReportWriter().run(report, results, filename, 'a/b/c.sedml/report1', format=format) + io.ReportWriter().run(report, results, filename, 'a/b/c.sedml/report2', format=format) + io.ReportWriter().run(report, results, filename, 'a/b/c.sedml/report3', format=format) + io.ReportWriter().run(report, results, filename, 'a/b/d.sedml/report4', format=format) + io.ReportWriter().run(report, results, filename, 'a/b/report5', format=format) + io.ReportWriter().run(report, results, filename, 'a/b/report6', format=format) self.assertEqual(io.ReportReader().get_ids(filename, format=format), set([ 'a/b/c.sedml/report1', diff --git a/tests/sedml/test_sedml_exec.py b/tests/sedml/test_sedml_exec.py index af1f4fca..5eb0f1ba 100644 --- a/tests/sedml/test_sedml_exec.py +++ b/tests/sedml/test_sedml_exec.py @@ -3,7 +3,7 @@ Status, CombineArchiveLog, SedDocumentLog, TaskLog, ReportLog) from biosimulators_utils.log.utils import init_sed_document_log from biosimulators_utils.plot.data_model import PlotFormat -from biosimulators_utils.report.data_model import VariableResults, ReportResults, ReportFormat +from biosimulators_utils.report.data_model import VariableResults, DataSetResults, ReportResults, ReportFormat from biosimulators_utils.report.io import ReportReader from biosimulators_utils.sedml import data_model from biosimulators_utils.sedml import exec @@ -196,43 +196,41 @@ def execute_task(task, variables, log): out_dir, report_formats=[ReportFormat.csv], plot_formats=[]) expected_output_results = ReportResults({ - doc.outputs[0].id: pandas.DataFrame( - numpy.array([ - numpy.array((1., 2.)), - numpy.array((5., 6.)), - ]), - index=['dataset_1', 'dataset_2'], - ), - doc.outputs[1].id: pandas.DataFrame( - numpy.array([ - numpy.array((3., 4.)), - numpy.array((7., 8.)), - ]), - index=['dataset_3', 'dataset_4'], - ), - doc.outputs[2].id: pandas.DataFrame( - numpy.array([ - numpy.array((1., 2.)), - ]), - index=['dataset_5'], - ), - doc.outputs[3].id: pandas.DataFrame( - numpy.array([ - numpy.array((7., 8.)), - numpy.array((7., 8.)), - ]), - index=['dataset_6', 'dataset_7'], - ), + doc.outputs[0].id: DataSetResults({ + 'dataset_1': numpy.array((1., 2.)), + 'dataset_2': numpy.array((5., 6.)), + }), + doc.outputs[1].id: DataSetResults({ + 'dataset_3': numpy.array((3., 4.)), + 'dataset_4': numpy.array((7., 8.)), + }), + doc.outputs[2].id: DataSetResults({ + 'dataset_5': numpy.array((1., 2.)), + }), + doc.outputs[3].id: DataSetResults({ + 'dataset_6': numpy.array((7., 8.)), + 'dataset_7': numpy.array((7., 8.)), + }), }) self.assertEqual(sorted(output_results.keys()), sorted(expected_output_results.keys())) - for key in output_results.keys(): - self.assertTrue(output_results[key].equals(expected_output_results[key])) - - df = ReportReader().run(out_dir, doc.outputs[0].id, format=ReportFormat.csv) - self.assertTrue(output_results[doc.outputs[0].id].equals(df)) - - df = ReportReader().run(out_dir, doc.outputs[1].id, format=ReportFormat.csv) - self.assertTrue(output_results[doc.outputs[1].id].equals(df)) + for report_id, data_set_results in output_results.items(): + self.assertEqual(sorted(output_results[report_id].keys()), sorted(expected_output_results[report_id].keys())) + for data_set_id in data_set_results.keys(): + numpy.testing.assert_allclose( + output_results[report_id][data_set_id], + expected_output_results[report_id][data_set_id]) + + data_set_results = ReportReader().run(doc.outputs[0], out_dir, doc.outputs[0].id, format=ReportFormat.csv) + for data_set in doc.outputs[0].data_sets: + numpy.testing.assert_allclose( + output_results[doc.outputs[0].id][data_set.id], + data_set_results[data_set.id]) + + data_set_results = ReportReader().run(doc.outputs[1], out_dir, doc.outputs[1].id, format=ReportFormat.csv) + for data_set in doc.outputs[1].data_sets: + numpy.testing.assert_allclose( + output_results[doc.outputs[1].id][data_set.id], + data_set_results[data_set.id]) # save in HDF5 format doc.models[1].source = doc.models[0].source @@ -240,11 +238,17 @@ def execute_task(task, variables, log): shutil.rmtree(out_dir) exec.exec_sed_doc(execute_task, filename, os.path.dirname(filename), out_dir, report_formats=[ReportFormat.h5], plot_formats=[]) - df = ReportReader().run(out_dir, doc.outputs[0].id, format=ReportFormat.h5) - self.assertTrue(output_results[doc.outputs[0].id].equals(df)) + data_set_results = ReportReader().run(doc.outputs[0], out_dir, doc.outputs[0].id, format=ReportFormat.h5) + for data_set in doc.outputs[0].data_sets: + numpy.testing.assert_allclose( + output_results[doc.outputs[0].id][data_set.id], + data_set_results[data_set.id]) - df = ReportReader().run(out_dir, doc.outputs[1].id, format=ReportFormat.h5) - self.assertTrue(output_results[doc.outputs[1].id].equals(df)) + data_set_results = ReportReader().run(doc.outputs[1], out_dir, doc.outputs[1].id, format=ReportFormat.h5) + for data_set in doc.outputs[1].data_sets: + numpy.testing.assert_allclose( + output_results[doc.outputs[1].id][data_set.id], + data_set_results[data_set.id]) # track execution status shutil.rmtree(out_dir) @@ -522,13 +526,13 @@ def execute_task(task, variables, log): out_dir = os.path.join(self.tmp_dir, 'results') report_results, _ = exec.exec_sed_doc(execute_task, filename, working_dir, out_dir, apply_xml_model_changes=False) - numpy.testing.assert_equal(report_results[doc.outputs[0].id].loc[doc.outputs[0].data_sets[0].id, :], numpy.array((1., ))) - numpy.testing.assert_equal(report_results[doc.outputs[0].id].loc[doc.outputs[0].data_sets[1].id, :], numpy.array((2., ))) + numpy.testing.assert_equal(report_results[doc.outputs[0].id][doc.outputs[0].data_sets[0].id], numpy.array((1., ))) + numpy.testing.assert_equal(report_results[doc.outputs[0].id][doc.outputs[0].data_sets[1].id], numpy.array((2., ))) report_results, _ = exec.exec_sed_doc(execute_task, filename, working_dir, out_dir, apply_xml_model_changes=True) - numpy.testing.assert_equal(report_results[doc.outputs[0].id].loc[doc.outputs[0].data_sets[0].id, :], numpy.array((2.5, ))) + numpy.testing.assert_equal(report_results[doc.outputs[0].id][doc.outputs[0].data_sets[0].id], numpy.array((2.5, ))) expected_value = 0.2 * 2.5 + 2.0 * 3.1 * 4.0 - numpy.testing.assert_equal(report_results[doc.outputs[0].id].loc[doc.outputs[0].data_sets[1].id, :], numpy.array((expected_value))) + numpy.testing.assert_equal(report_results[doc.outputs[0].id][doc.outputs[0].data_sets[1].id], numpy.array((expected_value))) def test_warnings(self): # no tasks @@ -890,8 +894,8 @@ def execute_task(task, variables, log): out_dir = os.path.join(self.tmp_dir, 'results') with self.assertWarnsRegex(UserWarning, 'do not have consistent shapes'): report_results, _ = exec.exec_sed_doc(execute_task, doc, working_dir, out_dir) - numpy.testing.assert_equal(report_results[doc.outputs[0].id].loc[doc.outputs[0].data_sets[0].id, :], numpy.array((1., numpy.nan))) - numpy.testing.assert_equal(report_results[doc.outputs[0].id].loc[doc.outputs[0].data_sets[1].id, :], numpy.array((1., 2.))) + numpy.testing.assert_equal(report_results[doc.outputs[0].id][doc.outputs[0].data_sets[0].id], numpy.array((1.))) + numpy.testing.assert_equal(report_results[doc.outputs[0].id][doc.outputs[0].data_sets[1].id], numpy.array((1., 2.))) doc.outputs[0].data_sets.append( data_model.DataSet( @@ -910,11 +914,11 @@ def execute_task(task, variables, log): with self.assertRaisesRegex(SedmlExecutionError, 'Must pass 2-d input'): with self.assertWarnsRegex(UserWarning, 'do not have consistent shapes'): report_results, _ = exec.exec_sed_doc(execute_task, doc, working_dir, out_dir) - # numpy.testing.assert_equal(report_results[doc.outputs[0].id].loc[doc.outputs[0].data_sets[0].id, :], + # numpy.testing.assert_equal(report_results[doc.outputs[0].id][doc.outputs[0].data_sets[0].id], # numpy.array(((1., numpy.nan, numpy.nan), (numpy.nan, numpy.nan, numpy.nan), (numpy.nan, numpy.nan, numpy.nan)))) - # numpy.testing.assert_equal(report_results[doc.outputs[0].id].loc[doc.outputs[0].data_sets[1].id, :], + # numpy.testing.assert_equal(report_results[doc.outputs[0].id][doc.outputs[0].data_sets[1].id], # numpy.array(((1., 2., numpy.nan), (numpy.nan, numpy.nan, numpy.nan), (numpy.nan, numpy.nan, numpy.nan)))) - # numpy.testing.assert_equal(report_results[doc.outputs[0].id].loc[doc.outputs[0].data_sets[2].id, :], + # numpy.testing.assert_equal(report_results[doc.outputs[0].id][doc.outputs[0].data_sets[2].id], # numpy.array(((1., 2., 3.), (4., 5., 6.), (7., 8., 9.)))) # warning: data set labels are not unique