diff --git a/docs/contributors.rst b/docs/contributors.rst index f3647cc6..74065209 100644 --- a/docs/contributors.rst +++ b/docs/contributors.rst @@ -14,3 +14,4 @@ Here are all the wonderful people that helped make this project happen * `Anton Travleev `_ * `@nicolaborate `_ * `@rzehumat `_ +* `Federico Grimaldi `_ diff --git a/serpentTools/parsers/sensitivity.py b/serpentTools/parsers/sensitivity.py index a0b6cd56..8c4ae959 100644 --- a/serpentTools/parsers/sensitivity.py +++ b/serpentTools/parsers/sensitivity.py @@ -516,6 +516,39 @@ def _getCleanedPertOpt(self, attrName, value): "{}".format(attrName, missing) ) + def toDataFrame(self, columns=["MAT", "ZAI", "PERT", "ENE", "S", "rel_unc", "RESP"]): + """ + Create a pandas DataFrame for the sensitivities + + + Parameters + ---------- + columns : sequence of str, optional + Names for the columns of the output dataframe. + Default is ``["MAT", "ZAI", "PERT", "ENE", "S", "rel_unc", "RESP"]`` + + Returns + ------- + pandas.DataFrame + 2D tabulated representation of the sensitivity. Columns reflect + materials, zais, perts, energies, sensitivity, relative uncertainty + and response + + """ + import pandas as pd + out = [] + size = self.nMat * self.nZai * self.nPert * self.nEne + idx = pd.MultiIndex.from_product([self.materials.keys(), + self.zais, + self.perts, + self.energies[1:]]) + for k, v in self.sensitivities.items(): + out.append(pd.DataFrame(v.reshape(size, 2), + index=idx + ).reset_index().assign(Response=k)) + out = pd.concat(out, ignore_index=True) + out.columns = columns + return out def reshapePermuteSensMat(vec, newShape): """ diff --git a/tests/test_sensivity.py b/tests/test_sensivity.py index ef27fc2f..cd85864c 100644 --- a/tests/test_sensivity.py +++ b/tests/test_sensivity.py @@ -1,6 +1,8 @@ """ Test the sensitivity reader """ +import pandas as pd + from unittest import TestCase, skipUnless from collections import OrderedDict from itertools import product @@ -201,6 +203,29 @@ def test_materials(self): actual = self.reader.materials self.assertDictEqual(expected, actual) + def test_toDataFrame(self): + fullFrame = self.reader.toDataFrame() + expected = { + 'keff': array([[ + [[3.21018000e-01, 7.40000000e-02], + [-5.58871000e-03, 1.00000000e+00], + [0.00000000e+00, 0.00000000e+00], + [-5.16380000e-03, 7.60000000e-01], + [-1.14412000e-01, 5.70000000e-02], + [4.46246000e-01, 4.20000000e-02], + [-6.34103000e-05, 1.00000000e+00]]]]) + } + assert fullFrame.columns == pd.Index(['MAT', 'ZAI', 'PERT', 'ENE', 'S', 'rel_unc', 'RESP']) + assert fullFrame.MAT.unique() == list(self.reader.materials.keys()) + assert (fullFrame.ZAI.unique() == list(self.reader.zais.keys())).all() + assert (fullFrame.PERT.unique() == list(self.reader.perts.keys())).all() + assert (fullFrame.ENE.unique() == self.reader.energies[1:]).all() + assert (fullFrame.query("RESP == 'keff'").S == expectd['keff'][:,0]).all() + assert (fullFrame.query("RESP == 'keff'").rel_unc == expectd['keff'][:,1]).all() + + columns = ["A", "B", "C", "D", "E", "F", "G"] + assert self.reader.toDataFrame(columns=columns).columns == columns + class SensitivityPlotTester(SensitivityTestHelper): """Class for testing rudimentary plot aspects of the reader.""" @@ -211,7 +236,7 @@ class SensitivityPlotTester(SensitivityTestHelper): def _plot(self, **kwargs): """Shortcut for plotting.""" - return self.reader.plot(self.RESP, **kwargs) + return (self.reader.plot(self.RESP, **kwargs)).all() def _checkAxisLabels(self, ax, xlabel, ylabel, msg=None): self.assertEqual(ax.get_xlabel(), xlabel, msg=msg)