Changeset d4dac80 in sasview for src/sas/qtgui/Perspectives/Fitting/FittingWidget.py
- Timestamp:
- Apr 25, 2018 6:59:33 AM (6 years ago)
- Branches:
- ESS_GUI, ESS_GUI_Docs, ESS_GUI_batch_fitting, ESS_GUI_bumps_abstraction, ESS_GUI_iss1116, ESS_GUI_iss879, ESS_GUI_iss959, ESS_GUI_opencl, ESS_GUI_ordering, ESS_GUI_sync_sascalc
- Children:
- 017b285
- Parents:
- 93c79b5
- git-author:
- Piotr Rozyczko <rozyczko@…> (03/22/18 16:46:19)
- git-committer:
- Piotr Rozyczko <rozyczko@…> (04/25/18 06:59:33)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
src/sas/qtgui/Perspectives/Fitting/FittingWidget.py
rded5e77 rd4dac80 3 3 from collections import defaultdict 4 4 5 5 import copy 6 6 import logging 7 7 import traceback … … 24 24 import sas.qtgui.Utilities.GuiUtils as GuiUtils 25 25 import sas.qtgui.Utilities.LocalConfig as LocalConfig 26 from sas.qtgui.Utilities.GridPanel import BatchOutputPanel27 26 from sas.qtgui.Utilities.CategoryInstaller import CategoryInstaller 28 27 from sas.qtgui.Plotting.PlotterData import Data1D … … 88 87 fittingFinishedSignal = QtCore.pyqtSignal(tuple) 89 88 batchFittingFinishedSignal = QtCore.pyqtSignal(tuple) 89 Calc1DFinishedSignal = QtCore.pyqtSignal(tuple) 90 Calc2DFinishedSignal = QtCore.pyqtSignal(tuple) 90 91 91 92 def __init__(self, parent=None, data=None, tab_id=1): … … 99 100 self.tab_id = tab_id 100 101 101 # Main Data[12]D holder102 self.logic = FittingLogic()103 104 102 # Globals 105 103 self.initializeGlobals() … … 107 105 # Set up desired logging level 108 106 logging.disable(LocalConfig.DISABLE_LOGGING) 107 108 # data index for the batch set 109 self.data_index = 0 110 # Main Data[12]D holders 111 # Logics.data contains a single Data1D/Data2D object 112 self._logic = [FittingLogic()] 109 113 110 114 # Main GUI setup up … … 134 138 self.initializeControls() 135 139 136 # Display HTML content137 #self.setupHelp()140 if data is not None: 141 self.data = data 138 142 139 143 # New font to display angstrom symbol … … 142 146 self.label_19.setStyleSheet(new_font) 143 147 144 self._index = None 145 if data is not None: 146 self.data = data 148 @property 149 def logic(self): 150 # make sure the logic contains at least one element 151 assert(self._logic) 152 # logic connected to the currently shown data 153 return self._logic[self.data_index] 147 154 148 155 @property … … 161 168 162 169 assert isinstance(value[0], QtGui.QStandardItem) 163 # _index contains the QIndex with data164 self._index = value[0]165 170 166 171 # Keep reference to all datasets for batch 167 172 self.all_data = value 168 173 169 # Update logics with data items 174 # Create logics with data items 175 self._logic=[] 170 176 # Logics.data contains only a single Data1D/Data2D object 171 self.logic.data = GuiUtils.dataFromItem(value[0]) 177 for data_item in value: 178 self._logic.append(FittingLogic()) 179 self._logic[-1].data = GuiUtils.dataFromItem(data_item) 172 180 173 181 # Overwrite data type descriptor … … 226 234 # Polydisp widget table default index for function combobox 227 235 self.orig_poly_index = 3 236 # copy of current kernel model 237 self.kernel_module_copy = None 228 238 229 239 # Page id for fitting … … 488 498 self.batchFittingFinishedSignal.connect(self.batchFitComplete) 489 499 self.fittingFinishedSignal.connect(self.fitComplete) 500 self.Calc1DFinishedSignal.connect(self.complete1D) 501 self.Calc2DFinishedSignal.connect(self.complete2D) 490 502 491 503 # Signals from separate tabs asking for replot … … 930 942 Update the logic based on the selected file in batch fitting 931 943 """ 932 self._index = self.all_data[data_index] 933 self.logic.data = GuiUtils.dataFromItem(self.all_data[data_index]) 944 self.data_index = data_index 934 945 self.updateQRange() 935 946 … … 1231 1242 return 1232 1243 1244 # keep local copy of kernel parameters, as they will change during the update 1245 self.kernel_module_copy = copy.deepcopy(self.kernel_module) 1246 1233 1247 # Create the fitting thread, based on the fitter 1234 1248 completefn = self.batchFittingCompleted if self.is_batch_fitting else self.fittingCompleted … … 1297 1311 #re-enable the Fit button 1298 1312 self.setFittingStopped() 1299 # Show the grid panel1300 self.grid_window = BatchOutputPanel(parent=self, output_data=result[0])1301 self.grid_window.show()1302 1303 def fittingCompleted(self, result):1304 """1305 Send the finish message from calculate threads to main thread1306 """1307 self.fittingFinishedSignal.emit(result)1308 1309 def fitComplete(self, result):1310 """1311 Receive and display fitting results1312 "result" is a tuple of actual result list and the fit time in seconds1313 """1314 #re-enable the Fit button1315 self.setFittingStopped()1316 1313 1317 1314 if result is None: … … 1320 1317 return 1321 1318 1319 # Show the grid panel 1320 self.communicate.sendDataToGridSignal.emit(result[0]) 1321 1322 elapsed = result[1] 1323 msg = "Fitting completed successfully in: %s s.\n" % GuiUtils.formatNumber(elapsed) 1324 self.communicate.statusBarUpdateSignal.emit(msg) 1325 1326 # Run over the list of results and update the items 1327 for res_index, res_list in enumerate(result[0]): 1328 # results 1329 res = res_list[0] 1330 param_dict = self.paramDictFromResults(res) 1331 1332 # create local kernel_module 1333 kernel_module = FittingUtilities.updateKernelWithResults(self.kernel_module, param_dict) 1334 # pull out current data 1335 data = self._logic[res_index].data 1336 1337 # Switch indexes 1338 self.onSelectBatchFilename(res_index) 1339 1340 method = self.complete1D if isinstance(self.data, Data1D) else self.complete2D 1341 self.calculateQGridForModelExt(data=data, model=kernel_module, completefn=method, use_threads=False) 1342 1343 # Restore original kernel_module, so subsequent fits on the same model don't pick up the new params 1344 if self.kernel_module is not None: 1345 self.kernel_module = copy.deepcopy(self.kernel_module_copy) 1346 1347 def paramDictFromResults(self, results): 1348 """ 1349 Given the fit results structure, pull out optimized parameters and return them as nicely 1350 formatted dict 1351 """ 1352 if results.fitness is None or \ 1353 not np.isfinite(results.fitness) or \ 1354 np.any(results.pvec is None) or \ 1355 not np.all(np.isfinite(results.pvec)): 1356 msg = "Fitting did not converge!" 1357 self.communicate.statusBarUpdateSignal.emit(msg) 1358 msg += results.mesg 1359 logging.error(msg) 1360 return 1361 1362 param_list = results.param_list # ['radius', 'radius.width'] 1363 param_values = results.pvec # array([ 0.36221662, 0.0146783 ]) 1364 param_stderr = results.stderr # array([ 1.71293015, 1.71294233]) 1365 params_and_errors = list(zip(param_values, param_stderr)) 1366 param_dict = dict(zip(param_list, params_and_errors)) 1367 1368 return param_dict 1369 1370 def fittingCompleted(self, result): 1371 """ 1372 Send the finish message from calculate threads to main thread 1373 """ 1374 self.fittingFinishedSignal.emit(result) 1375 1376 def fitComplete(self, result): 1377 """ 1378 Receive and display fitting results 1379 "result" is a tuple of actual result list and the fit time in seconds 1380 """ 1381 #re-enable the Fit button 1382 self.setFittingStopped() 1383 1384 if result is None: 1385 msg = "Fitting failed." 1386 self.communicate.statusBarUpdateSignal.emit(msg) 1387 return 1388 1322 1389 res_list = result[0][0] 1323 1390 res = res_list[0] 1324 if res.fitness is None or \ 1325 not np.isfinite(res.fitness) or \ 1326 np.any(res.pvec is None) or \ 1327 not np.all(np.isfinite(res.pvec)): 1328 msg = "Fitting did not converge!" 1329 self.communicate.statusBarUpdateSignal.emit(msg) 1330 msg += res.mesg 1331 logging.error(msg) 1332 return 1391 self.chi2 = res.fitness 1392 param_dict = self.paramDictFromResults(res) 1333 1393 1334 1394 elapsed = result[1] … … 1339 1399 msg = "Fitting completed successfully in: %s s." % GuiUtils.formatNumber(elapsed) 1340 1400 self.communicate.statusBarUpdateSignal.emit(msg) 1341 1342 self.chi2 = res.fitness1343 param_list = res.param_list # ['radius', 'radius.width']1344 param_values = res.pvec # array([ 0.36221662, 0.0146783 ])1345 param_stderr = res.stderr # array([ 1.71293015, 1.71294233])1346 params_and_errors = list(zip(param_values, param_stderr))1347 param_dict = dict(zip(param_list, params_and_errors))1348 1401 1349 1402 # Dictionary of fitted parameter: value, error … … 2001 2054 fitted_data.symbol = 'Line' 2002 2055 # Notify the GUI manager so it can update the main model in DataExplorer 2003 GuiUtils.updateModelItemWithPlot(self. _index, fitted_data, name)2056 GuiUtils.updateModelItemWithPlot(self.all_data[self.data_index], fitted_data, name) 2004 2057 2005 2058 def createTheoryIndex(self, fitted_data): … … 2031 2084 def methodCompleteForData(self): 2032 2085 '''return the method for result parsin on calc complete ''' 2033 return self.complete1D if isinstance(self.data, Data1D) else self.complete2D 2034 2035 def calculateQGridForModel(self): 2036 """ 2037 Prepare the fitting data object, based on current ModelModel 2038 """ 2039 if self.kernel_module is None: 2040 return 2086 return self.completed1D if isinstance(self.data, Data1D) else self.completed2D 2087 2088 def calculateQGridForModelExt(self, data=None, model=None, completefn=None, use_threads=True): 2089 """ 2090 Wrapper for Calc1D/2D calls 2091 """ 2092 if data is None: 2093 data = self.data 2094 if model is None: 2095 model = self.kernel_module 2096 if completefn is None: 2097 completefn = self.methodCompleteForData() 2098 2041 2099 # Awful API to a backend method. 2042 method = self.methodCalculateForData()(data=self.data,2043 model= self.kernel_module,2100 calc_thread = self.methodCalculateForData()(data=data, 2101 model=model, 2044 2102 page_id=0, 2045 2103 qmin=self.q_range_min, … … 2050 2108 fid=None, 2051 2109 toggle_mode_on=False, 2052 completefn= None,2110 completefn=completefn, 2053 2111 update_chisqr=True, 2054 2112 exception_handler=self.calcException, 2055 2113 source=None) 2056 2057 calc_thread = threads.deferToThread(method.compute) 2058 calc_thread.addCallback(self.methodCompleteForData()) 2059 calc_thread.addErrback(self.calculateDataFailed) 2114 if use_threads: 2115 if LocalConfig.USING_TWISTED: 2116 # start the thread with twisted 2117 thread = threads.deferToThread(calc_thread.compute) 2118 thread.addCallback(completefn) 2119 thread.addErrback(self.calculateDataFailed) 2120 else: 2121 # Use the old python threads + Queue 2122 calc_thread.queue() 2123 calc_thread.ready(2.5) 2124 else: 2125 results = calc_thread.compute() 2126 completefn(results) 2127 2128 def calculateQGridForModel(self): 2129 """ 2130 Prepare the fitting data object, based on current ModelModel 2131 """ 2132 if self.kernel_module is None: 2133 return 2134 self.calculateQGridForModelExt() 2060 2135 2061 2136 def calculateDataFailed(self, reason): … … 2064 2139 """ 2065 2140 print("Calculate Data failed with ", reason) 2141 2142 def completed1D(self, return_data): 2143 self.Calc1DFinishedSignal.emit(return_data) 2144 2145 def completed2D(self, return_data): 2146 self.Calc2DFinishedSignal.emit(return_data) 2066 2147 2067 2148 def complete1D(self, return_data): … … 2107 2188 residuals_plot.id = "Residual " + residuals_plot.id 2108 2189 self.createNewIndex(residuals_plot) 2109 #self.communicate.plotUpdateSignal.emit([residuals_plot])2110 2190 2111 2191 def calcException(self, etype, value, tb):
Note: See TracChangeset
for help on using the changeset viewer.