[7248d75d] | 1 | import sys |
---|
| 2 | import unittest |
---|
[fde5bcd] | 3 | from PyQt5 import QtGui, QtCore |
---|
[7248d75d] | 4 | |
---|
[dc5ef15] | 5 | from sas.qtgui.Plotting.PlotterData import Data1D |
---|
| 6 | from sas.qtgui.Plotting.PlotterData import Data2D |
---|
[6fd4e36] | 7 | |
---|
[7248d75d] | 8 | from UnitTesting.TestUtils import WarningTestNotImplemented |
---|
[6fd4e36] | 9 | |
---|
[7248d75d] | 10 | from sasmodels import generate |
---|
| 11 | from sasmodels import modelinfo |
---|
[06b0138] | 12 | from sasmodels.sasview_model import load_standard_models |
---|
[7248d75d] | 13 | |
---|
| 14 | # Tested module |
---|
| 15 | from sas.qtgui.Perspectives.Fitting import FittingUtilities |
---|
| 16 | |
---|
| 17 | class FittingUtilitiesTest(unittest.TestCase): |
---|
| 18 | '''Test the Fitting Utilities functions''' |
---|
| 19 | def setUp(self): |
---|
| 20 | '''Empty''' |
---|
| 21 | pass |
---|
| 22 | |
---|
| 23 | def tearDown(self): |
---|
| 24 | '''Empty''' |
---|
| 25 | pass |
---|
| 26 | |
---|
| 27 | def testReplaceShellName(self): |
---|
| 28 | """ |
---|
| 29 | Test the utility function for string manipulation |
---|
| 30 | """ |
---|
| 31 | param_name = "test [123]" |
---|
| 32 | value = "replaced" |
---|
| 33 | result = FittingUtilities.replaceShellName(param_name, value) |
---|
| 34 | |
---|
| 35 | self.assertEqual(result, "test replaced") |
---|
| 36 | |
---|
| 37 | # Assert! |
---|
| 38 | param_name = "no brackets" |
---|
| 39 | with self.assertRaises(AssertionError): |
---|
| 40 | result = FittingUtilities.replaceShellName(param_name, value) |
---|
| 41 | |
---|
| 42 | |
---|
| 43 | def testGetIterParams(self): |
---|
| 44 | """ |
---|
| 45 | Assure the right multishell parameters are returned |
---|
| 46 | """ |
---|
| 47 | # Use a single-shell parameter |
---|
| 48 | model_name = "barbell" |
---|
| 49 | kernel_module = generate.load_kernel_module(model_name) |
---|
| 50 | barbell_parameters = modelinfo.make_parameter_table(getattr(kernel_module, 'parameters', [])) |
---|
| 51 | |
---|
| 52 | params = FittingUtilities.getIterParams(barbell_parameters) |
---|
| 53 | # returns empty list |
---|
| 54 | self.assertEqual(params, []) |
---|
| 55 | |
---|
| 56 | # Use a multi-shell parameter |
---|
| 57 | model_name = "core_multi_shell" |
---|
| 58 | kernel_module = generate.load_kernel_module(model_name) |
---|
| 59 | multishell_parameters = modelinfo.make_parameter_table(getattr(kernel_module, 'parameters', [])) |
---|
| 60 | |
---|
| 61 | params = FittingUtilities.getIterParams(multishell_parameters) |
---|
| 62 | # returns a non-empty list |
---|
| 63 | self.assertNotEqual(params, []) |
---|
| 64 | self.assertIn('sld', str(params)) |
---|
| 65 | self.assertIn('thickness', str(params)) |
---|
| 66 | |
---|
| 67 | def testGetMultiplicity(self): |
---|
| 68 | """ |
---|
| 69 | Assure more multishell parameters are evaluated correctly |
---|
| 70 | """ |
---|
| 71 | # Use a single-shell parameter |
---|
| 72 | model_name = "barbell" |
---|
| 73 | kernel_module = generate.load_kernel_module(model_name) |
---|
| 74 | barbell_parameters = modelinfo.make_parameter_table(getattr(kernel_module, 'parameters', [])) |
---|
| 75 | |
---|
| 76 | param_name, param_length = FittingUtilities.getMultiplicity(barbell_parameters) |
---|
| 77 | # returns nothing |
---|
| 78 | self.assertEqual(param_name, "") |
---|
| 79 | self.assertEqual(param_length, 0) |
---|
| 80 | |
---|
| 81 | # Use a multi-shell parameter |
---|
| 82 | model_name = "core_multi_shell" |
---|
| 83 | kernel_module = generate.load_kernel_module(model_name) |
---|
| 84 | multishell_parameters = modelinfo.make_parameter_table(getattr(kernel_module, 'parameters', [])) |
---|
| 85 | |
---|
| 86 | param_name, param_length = FittingUtilities.getMultiplicity(multishell_parameters) |
---|
| 87 | |
---|
| 88 | self.assertEqual(param_name, "n") |
---|
| 89 | self.assertEqual(param_length, 10) |
---|
| 90 | |
---|
| 91 | def testAddParametersToModel(self): |
---|
| 92 | """ |
---|
| 93 | Checks the QModel update from Sasmodel parameters |
---|
| 94 | """ |
---|
| 95 | # Use a single-shell parameter |
---|
| 96 | model_name = "barbell" |
---|
[06b0138] | 97 | models = load_standard_models() |
---|
| 98 | |
---|
[7248d75d] | 99 | kernel_module = generate.load_kernel_module(model_name) |
---|
[06b0138] | 100 | kernel_module_o = None |
---|
| 101 | for model in models: |
---|
| 102 | if model.name == model_name: |
---|
| 103 | kernel_module_o = model() |
---|
| 104 | self.assertIsNotNone(kernel_module_o) |
---|
[7248d75d] | 105 | barbell_parameters = modelinfo.make_parameter_table(getattr(kernel_module, 'parameters', [])) |
---|
| 106 | |
---|
[06b0138] | 107 | params = FittingUtilities.addParametersToModel(barbell_parameters, kernel_module_o, True) |
---|
[7248d75d] | 108 | |
---|
| 109 | # Test the resulting model |
---|
[9208346] | 110 | self.assertEqual(len(params), 7) |
---|
| 111 | self.assertEqual(len(params[0]), 5) |
---|
| 112 | self.assertTrue(params[0][0].isCheckable()) |
---|
| 113 | self.assertEqual(params[0][0].text(), "sld") |
---|
| 114 | self.assertEqual(params[1][0].text(), "sld_solvent") |
---|
[7248d75d] | 115 | |
---|
| 116 | # Use a multi-shell parameter to see that the method includes shell params |
---|
| 117 | model_name = "core_multi_shell" |
---|
| 118 | kernel_module = generate.load_kernel_module(model_name) |
---|
[06b0138] | 119 | kernel_module_o = None |
---|
| 120 | for model in models: |
---|
| 121 | if model.name == model_name: |
---|
| 122 | kernel_module_o = model() |
---|
| 123 | self.assertIsNotNone(kernel_module_o) |
---|
[7248d75d] | 124 | multi_parameters = modelinfo.make_parameter_table(getattr(kernel_module, 'parameters', [])) |
---|
| 125 | |
---|
[06b0138] | 126 | params = FittingUtilities.addParametersToModel(multi_parameters, kernel_module_o, False) |
---|
[7248d75d] | 127 | |
---|
| 128 | # Test the resulting model |
---|
[9208346] | 129 | self.assertEqual(len(params), 3) |
---|
| 130 | self.assertEqual(len(params[0]), 5) |
---|
| 131 | self.assertTrue(params[0][0].isCheckable()) |
---|
| 132 | self.assertEqual(params[0][0].text(), "sld_core") |
---|
| 133 | self.assertEqual(params[1][0].text(), "radius") |
---|
[7248d75d] | 134 | |
---|
| 135 | def testAddSimpleParametersToModel(self): |
---|
| 136 | """ |
---|
| 137 | Checks the QModel update from Sasmodel parameters - no polydisp |
---|
| 138 | """ |
---|
| 139 | # Use a multi-shell parameter to see that the method doesn't include shells |
---|
| 140 | model_name = "core_multi_shell" |
---|
| 141 | kernel_module = generate.load_kernel_module(model_name) |
---|
[06b0138] | 142 | models = load_standard_models() |
---|
| 143 | kernel_module_o = None |
---|
| 144 | for model in models: |
---|
| 145 | if model.name == model_name: |
---|
| 146 | kernel_module_o = model() |
---|
| 147 | self.assertIsNotNone(kernel_module_o) |
---|
[7248d75d] | 148 | multi_parameters = modelinfo.make_parameter_table(getattr(kernel_module, 'parameters', [])) |
---|
| 149 | |
---|
[06b0138] | 150 | params = FittingUtilities.addParametersToModel(multi_parameters, kernel_module_o, True) |
---|
[7248d75d] | 151 | |
---|
| 152 | # Test the resulting model |
---|
[9208346] | 153 | self.assertEqual(len(params), 3) |
---|
| 154 | self.assertEqual(len(params[0]), 5) |
---|
| 155 | self.assertTrue(params[0][0].isCheckable()) |
---|
| 156 | self.assertEqual(params[0][0].text(), "sld_core") |
---|
| 157 | self.assertEqual(params[1][0].text(), "radius") |
---|
[7248d75d] | 158 | |
---|
| 159 | def testAddCheckedListToModel(self): |
---|
| 160 | """ |
---|
| 161 | Test for inserting a checkboxed item into a QModel |
---|
| 162 | """ |
---|
| 163 | model = QtGui.QStandardItemModel() |
---|
| 164 | params = ["row1", "row2", "row3"] |
---|
| 165 | |
---|
| 166 | FittingUtilities.addCheckedListToModel(model, params) |
---|
| 167 | |
---|
| 168 | # Check the model |
---|
| 169 | self.assertEqual(model.rowCount(), 1) |
---|
| 170 | self.assertTrue(model.item(0).isCheckable()) |
---|
| 171 | self.assertEqual(model.item(0, 0).text(), params[0]) |
---|
| 172 | self.assertEqual(model.item(0, 1).text(), params[1]) |
---|
| 173 | self.assertEqual(model.item(0, 2).text(), params[2]) |
---|
| 174 | |
---|
| 175 | def testAddShellsToModel(self): |
---|
| 176 | """ |
---|
| 177 | Test for inserting a list of QItems into a model |
---|
| 178 | """ |
---|
| 179 | # Use a multi-shell parameter to see that the method doesn't include shells |
---|
| 180 | model_name = "core_multi_shell" |
---|
| 181 | kernel_module = generate.load_kernel_module(model_name) |
---|
| 182 | multi_parameters = modelinfo.make_parameter_table(getattr(kernel_module, 'parameters', [])) |
---|
| 183 | |
---|
| 184 | model = QtGui.QStandardItemModel() |
---|
| 185 | |
---|
| 186 | index = 2 |
---|
| 187 | FittingUtilities.addShellsToModel(multi_parameters, model, index) |
---|
| 188 | # There should be index*len(multi_parameters) new rows |
---|
| 189 | self.assertEqual(model.rowCount(), 4) |
---|
| 190 | |
---|
| 191 | model = QtGui.QStandardItemModel() |
---|
| 192 | index = 5 |
---|
| 193 | FittingUtilities.addShellsToModel(multi_parameters, model, index) |
---|
| 194 | self.assertEqual(model.rowCount(), 10) |
---|
| 195 | |
---|
| 196 | self.assertEqual(model.item(1).child(0).text(), "Polydispersity") |
---|
| 197 | self.assertEqual(model.item(1).child(0).child(0).text(), "Distribution") |
---|
| 198 | self.assertEqual(model.item(1).child(0).child(0,1).text(), "40.0") |
---|
| 199 | |
---|
[6fd4e36] | 200 | def testCalculate1DChi2(self): |
---|
| 201 | """ |
---|
| 202 | Test the chi2 calculator for Data1D |
---|
| 203 | """ |
---|
| 204 | reference_data = Data1D(x=[0.1, 0.2], y=[0.0, 0.0]) |
---|
| 205 | |
---|
| 206 | # 1. identical data |
---|
| 207 | current_data = Data1D(x=[0.1, 0.2], y=[0.0, 0.0]) |
---|
| 208 | |
---|
| 209 | chi = FittingUtilities.calculateChi2(reference_data, current_data) |
---|
| 210 | |
---|
| 211 | # Should be zero |
---|
| 212 | self.assertAlmostEqual(chi, 0.0, 8) |
---|
| 213 | |
---|
| 214 | # 2. far data |
---|
| 215 | current_data = Data1D(x=[0.1, 0.2], y=[200.0, 150.0]) |
---|
| 216 | |
---|
| 217 | chi = FittingUtilities.calculateChi2(reference_data, current_data) |
---|
| 218 | |
---|
| 219 | # Should not be zero |
---|
| 220 | self.assertAlmostEqual(chi, 31250.0, 8) |
---|
| 221 | |
---|
| 222 | # 3. Wrong data |
---|
| 223 | current_data = Data1D(x=[0.1, 0.2], y=[200.0, 150.0, 200.0]) |
---|
| 224 | chi = FittingUtilities.calculateChi2(reference_data, current_data) |
---|
| 225 | # Should be None |
---|
| 226 | self.assertIsNone(chi) |
---|
| 227 | |
---|
| 228 | def testCalculate2DChi2(self): |
---|
| 229 | """ |
---|
| 230 | Test the chi2 calculator for Data2D |
---|
| 231 | """ |
---|
| 232 | reference_data = Data2D(image=[1.0, 2.0, 3.0], |
---|
| 233 | err_image=[0.01, 0.02, 0.03], |
---|
| 234 | qx_data=[0.1, 0.2, 0.3], |
---|
| 235 | qy_data=[0.1, 0.2, 0.3]) |
---|
| 236 | |
---|
| 237 | # 1. identical data |
---|
| 238 | current_data = Data2D(image=[1.0, 2.0, 3.0], |
---|
| 239 | err_image=[0.01, 0.02, 0.03], |
---|
| 240 | qx_data=[0.1, 0.2, 0.3], |
---|
| 241 | qy_data=[0.1, 0.2, 0.3]) |
---|
| 242 | |
---|
| 243 | chi = FittingUtilities.calculateChi2(reference_data, current_data) |
---|
| 244 | |
---|
| 245 | # Should be zero |
---|
| 246 | self.assertAlmostEqual(chi, 0.0, 8) |
---|
| 247 | |
---|
| 248 | # 2. far data |
---|
| 249 | current_data = Data2D(image=[100.0, 200.0, 300.0], |
---|
| 250 | err_image=[1.01, 2.02, 3.03], |
---|
| 251 | qx_data=[0.1, 0.2, 0.3], |
---|
| 252 | qy_data=[100.0, 200., 300.]) |
---|
| 253 | |
---|
| 254 | chi = FittingUtilities.calculateChi2(reference_data, current_data) |
---|
| 255 | |
---|
| 256 | # Should not be zero |
---|
| 257 | self.assertAlmostEqual(chi, 9607.88, 2) |
---|
| 258 | |
---|
| 259 | # 3. Wrong data |
---|
| 260 | current_data = Data2D(image=[1.0, 2.0, 3.0], |
---|
| 261 | err_image=[0.01, 0.02], |
---|
| 262 | qx_data=[0.1, 0.2], |
---|
| 263 | qy_data=[0.1, 0.2, 0.3]) |
---|
| 264 | # Should throw |
---|
| 265 | with self.assertRaises(ValueError): |
---|
| 266 | chi = FittingUtilities.calculateChi2(reference_data, current_data) |
---|
| 267 | |
---|
[fde5bcd] | 268 | def notestAddHeadersToModel(self): |
---|
| 269 | '''Check to see if headers are correctly applied''' |
---|
| 270 | #test model |
---|
| 271 | model = QtGui.QStandardItemModel() |
---|
| 272 | FittingUtilities.addHeadersToModel(model) |
---|
| 273 | |
---|
| 274 | # Assure we have properly named columns |
---|
| 275 | names = FittingUtilities.model_header_captions |
---|
| 276 | names_from_model = [model.headerData(i, QtCore.Qt.Horizontal) for i in range(len(names))] |
---|
| 277 | self.assertEqual(names, names_from_model) |
---|
| 278 | |
---|
| 279 | # Add another model |
---|
| 280 | model2 = QtGui.QStandardItemModel() |
---|
| 281 | # Add headers again |
---|
| 282 | FittingUtilities.addHeadersToModel(model2) |
---|
| 283 | # We still should have only the original names |
---|
| 284 | names_from_model2 = [model2.headerData(i, QtCore.Qt.Horizontal) for i in range(len(names))] |
---|
| 285 | self.assertEqual(names, names_from_model2) |
---|
| 286 | |
---|
| 287 | |
---|
[7248d75d] | 288 | if __name__ == "__main__": |
---|
| 289 | unittest.main() |
---|