Ignore:
Timestamp:
Apr 25, 2018 6:59:33 AM (6 years ago)
Author:
Piotr Rozyczko <rozyczko@…>
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)
Message:

Merge branch 'ESS_GUI' into ESS_GUI_better_batch

File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/sas/qtgui/Perspectives/Fitting/FittingWidget.py

    rded5e77 rd4dac80  
    33from collections import defaultdict 
    44 
    5  
     5import copy 
    66import logging 
    77import traceback 
     
    2424import sas.qtgui.Utilities.GuiUtils as GuiUtils 
    2525import sas.qtgui.Utilities.LocalConfig as LocalConfig 
    26 from sas.qtgui.Utilities.GridPanel import BatchOutputPanel 
    2726from sas.qtgui.Utilities.CategoryInstaller import CategoryInstaller 
    2827from sas.qtgui.Plotting.PlotterData import Data1D 
     
    8887    fittingFinishedSignal = QtCore.pyqtSignal(tuple) 
    8988    batchFittingFinishedSignal = QtCore.pyqtSignal(tuple) 
     89    Calc1DFinishedSignal = QtCore.pyqtSignal(tuple) 
     90    Calc2DFinishedSignal = QtCore.pyqtSignal(tuple) 
    9091 
    9192    def __init__(self, parent=None, data=None, tab_id=1): 
     
    99100        self.tab_id = tab_id 
    100101 
    101         # Main Data[12]D holder 
    102         self.logic = FittingLogic() 
    103  
    104102        # Globals 
    105103        self.initializeGlobals() 
     
    107105        # Set up desired logging level 
    108106        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()] 
    109113 
    110114        # Main GUI setup up 
     
    134138        self.initializeControls() 
    135139 
    136         # Display HTML content 
    137         #self.setupHelp() 
     140        if data is not None: 
     141            self.data = data 
    138142 
    139143        # New font to display angstrom symbol 
     
    142146        self.label_19.setStyleSheet(new_font) 
    143147 
    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] 
    147154 
    148155    @property 
     
    161168 
    162169        assert isinstance(value[0], QtGui.QStandardItem) 
    163         # _index contains the QIndex with data 
    164         self._index = value[0] 
    165170 
    166171        # Keep reference to all datasets for batch 
    167172        self.all_data = value 
    168173 
    169         # Update logics with data items 
     174        # Create logics with data items 
     175        self._logic=[] 
    170176        # 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) 
    172180 
    173181        # Overwrite data type descriptor 
     
    226234        # Polydisp widget table default index for function combobox 
    227235        self.orig_poly_index = 3 
     236        # copy of current kernel model 
     237        self.kernel_module_copy = None 
    228238 
    229239        # Page id for fitting 
     
    488498        self.batchFittingFinishedSignal.connect(self.batchFitComplete) 
    489499        self.fittingFinishedSignal.connect(self.fitComplete) 
     500        self.Calc1DFinishedSignal.connect(self.complete1D) 
     501        self.Calc2DFinishedSignal.connect(self.complete2D) 
    490502 
    491503        # Signals from separate tabs asking for replot 
     
    930942        Update the logic based on the selected file in batch fitting 
    931943        """ 
    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 
    934945        self.updateQRange() 
    935946 
     
    12311242            return 
    12321243 
     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 
    12331247        # Create the fitting thread, based on the fitter 
    12341248        completefn = self.batchFittingCompleted if self.is_batch_fitting else self.fittingCompleted 
     
    12971311        #re-enable the Fit button 
    12981312        self.setFittingStopped() 
    1299         # Show the grid panel 
    1300         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 thread 
    1306         """ 
    1307         self.fittingFinishedSignal.emit(result) 
    1308  
    1309     def fitComplete(self, result): 
    1310         """ 
    1311         Receive and display fitting results 
    1312         "result" is a tuple of actual result list and the fit time in seconds 
    1313         """ 
    1314         #re-enable the Fit button 
    1315         self.setFittingStopped() 
    13161313 
    13171314        if result is None: 
     
    13201317            return 
    13211318 
     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 
    13221389        res_list = result[0][0] 
    13231390        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) 
    13331393 
    13341394        elapsed = result[1] 
     
    13391399            msg = "Fitting completed successfully in: %s s." % GuiUtils.formatNumber(elapsed) 
    13401400        self.communicate.statusBarUpdateSignal.emit(msg) 
    1341  
    1342         self.chi2 = res.fitness 
    1343         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)) 
    13481401 
    13491402        # Dictionary of fitted parameter: value, error 
     
    20012054            fitted_data.symbol = 'Line' 
    20022055        # 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) 
    20042057 
    20052058    def createTheoryIndex(self, fitted_data): 
     
    20312084    def methodCompleteForData(self): 
    20322085        '''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 
    20412099        # 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, 
    20442102                                               page_id=0, 
    20452103                                               qmin=self.q_range_min, 
     
    20502108                                               fid=None, 
    20512109                                               toggle_mode_on=False, 
    2052                                                completefn=None, 
     2110                                               completefn=completefn, 
    20532111                                               update_chisqr=True, 
    20542112                                               exception_handler=self.calcException, 
    20552113                                               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() 
    20602135 
    20612136    def calculateDataFailed(self, reason): 
     
    20642139        """ 
    20652140        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) 
    20662147 
    20672148    def complete1D(self, return_data): 
     
    21072188        residuals_plot.id = "Residual " + residuals_plot.id 
    21082189        self.createNewIndex(residuals_plot) 
    2109         #self.communicate.plotUpdateSignal.emit([residuals_plot]) 
    21102190 
    21112191    def calcException(self, etype, value, tb): 
Note: See TracChangeset for help on using the changeset viewer.