Changeset d4dac80 in sasview for src/sas/qtgui/Perspectives/Fitting


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

Location:
src/sas/qtgui/Perspectives/Fitting
Files:
6 edited

Legend:

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

    r8b480d27 rd4dac80  
    358358        elapsed = result[1] 
    359359 
    360         # ADD THE BATCH FIT VIEW HERE 
    361         # 
     360        if result is None: 
     361            msg = "Fitting failed." 
     362            self.parent.communicate.statusBarUpdateSignal.emit(msg) 
     363            return 
     364 
     365        # Show the grid panel 
     366        self.parent.communicate.sendDataToGridSignal.emit(result[0]) 
    362367 
    363368        msg = "Fitting completed successfully in: %s s.\n" % GuiUtils.formatNumber(elapsed) 
  • src/sas/qtgui/Perspectives/Fitting/FittingUtilities.py

    rfde5bcd rd4dac80  
    421421        weight = numpy.abs(data) 
    422422    return weight 
     423 
     424def updateKernelWithResults(kernel, results): 
     425    """ 
     426    Takes model kernel and applies results dict to its parameters, 
     427    returning the modified (deep) copy of the kernel. 
     428    """ 
     429    assert(isinstance(results, dict)) 
     430    local_kernel = copy.deepcopy(kernel) 
     431 
     432    for parameter in results.keys(): 
     433        # Update the parameter value - note: this supports +/-inf as well 
     434        local_kernel.setParam(parameter, results[parameter][0]) 
     435 
     436    return local_kernel 
     437 
     438 
  • 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): 
  • src/sas/qtgui/Perspectives/Fitting/ModelThread.py

    rcee5c78 rd4dac80  
    88from sas.sascalc.data_util.calcthread import CalcThread 
    99from sas.sascalc.fit.MultiplicationModel import MultiplicationModel 
     10import sas.qtgui.Utilities.LocalConfig as LocalConfig 
    1011 
    1112class Calc2D(CalcThread): 
     
    99100        output[index_model] = value 
    100101        elapsed = time.time() - self.starttime 
    101         #self.complete(image=output, 
    102         #               data=self.data, 
    103         #               page_id=self.page_id, 
    104         #               model=self.model, 
    105         #               state=self.state, 
    106         #               toggle_mode_on=self.toggle_mode_on, 
    107         #               elapsed=elapsed, 
    108         #               index=index_model, 
    109         #               fid=self.fid, 
    110         #               qmin=self.qmin, 
    111         #               qmax=self.qmax, 
    112         #               weight=self.weight, 
    113         #               #qstep=self.qstep, 
    114         #               update_chisqr=self.update_chisqr, 
    115         #               source=self.source) 
    116         return (output, 
    117                 self.data, 
    118                 self.page_id, 
    119                 self.model, 
    120                 self.state, 
    121                 self.toggle_mode_on, 
    122                 elapsed, 
    123                 index_model, 
    124                 self.fid, 
    125                 self.qmin, 
    126                 self.qmax, 
    127                 self.weight, 
    128                 self.update_chisqr, 
    129                 self.source) 
     102 
     103        if LocalConfig.USING_TWISTED: 
     104            return (output, 
     105                    self.data, 
     106                    self.page_id, 
     107                    self.model, 
     108                    self.state, 
     109                    self.toggle_mode_on, 
     110                    elapsed, 
     111                    index_model, 
     112                    self.fid, 
     113                    self.qmin, 
     114                    self.qmax, 
     115                    self.weight, 
     116                    self.update_chisqr, 
     117                    self.source) 
     118        else: 
     119            self.complete(image=output, 
     120                           data=self.data, 
     121                           page_id=self.page_id, 
     122                           model=self.model, 
     123                           state=self.state, 
     124                           toggle_mode_on=self.toggle_mode_on, 
     125                           elapsed=elapsed, 
     126                           index=index_model, 
     127                           fid=self.fid, 
     128                           qmin=self.qmin, 
     129                           qmax=self.qmax, 
     130                           weight=self.weight, 
     131                           #qstep=self.qstep, 
     132                           update_chisqr=self.update_chisqr, 
     133                           source=self.source) 
    130134 
    131135 
     
    229233        elapsed = time.time() - self.starttime 
    230234 
    231         return (self.data.x[index], output[index], 
    232                 self.page_id, 
    233                 self.state, 
    234                 self.weight, 
    235                 self.fid, 
    236                 self.toggle_mode_on, 
    237                 elapsed, index, self.model, 
    238                 self.data, 
    239                 self.update_chisqr, 
    240                 self.source) 
    241  
    242         # TODO: as of 4.1, the output contains more items: 
    243         # unsmeared_* and pq_model/sq_model 
    244         # Need to add these too 
    245  
    246         #self.complete(x=self.data.x[index], y=output[index], 
    247         #              page_id=self.page_id, 
    248         #              state=self.state, 
    249         #              weight=self.weight, 
    250         #              fid=self.fid, 
    251         #              toggle_mode_on=self.toggle_mode_on, 
    252         #              elapsed=elapsed, index=index, model=self.model, 
    253         #              data=self.data, 
    254         #              update_chisqr=self.update_chisqr, 
    255         #              source=self.source, 
    256         #              unsmeared_model=unsmeared_output, 
    257         #              unsmeared_data=unsmeared_data, 
    258         #              unsmeared_error=unsmeared_error, 
    259         #              pq_model=pq_values, 
    260         #              sq_model=sq_values) 
     235        if LocalConfig.USING_TWISTED: 
     236            return (self.data.x[index], output[index], 
     237                    self.page_id, 
     238                    self.state, 
     239                    self.weight, 
     240                    self.fid, 
     241                    self.toggle_mode_on, 
     242                    elapsed, index, self.model, 
     243                    self.data, 
     244                    self.update_chisqr, 
     245                    self.source) 
     246        else: 
     247            self.complete(x=self.data.x[index], y=output[index], 
     248                          page_id=self.page_id, 
     249                          state=self.state, 
     250                          weight=self.weight, 
     251                          fid=self.fid, 
     252                          toggle_mode_on=self.toggle_mode_on, 
     253                          elapsed=elapsed, index=index, model=self.model, 
     254                          data=self.data, 
     255                          update_chisqr=self.update_chisqr, 
     256                          source=self.source, 
     257                          unsmeared_model=unsmeared_output, 
     258                          unsmeared_data=unsmeared_data, 
     259                          unsmeared_error=unsmeared_error, 
     260                          pq_model=pq_values, 
     261                          sq_model=sq_values) 
    261262 
    262263    def results(self): 
  • src/sas/qtgui/Perspectives/Fitting/ModelUtilities.py

    r3b3b40b rd4dac80  
    173173        return {} 
    174174 
    175     plugin_log("looking for models in: %s" % str(directory)) 
    176     # compile_file(directory)  #always recompile the folder plugin 
    177     logging.info("plugin model dir: %s" % str(directory)) 
    178  
    179175    plugins = {} 
    180176    for filename in os.listdir(directory): 
  • src/sas/qtgui/Perspectives/Fitting/UI/FittingWidgetUI.ui

    r3b3b40b rd4dac80  
    216216            </property> 
    217217            <property name="toolTip"> 
    218              <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Switch on magnetic scattering parameters.&lt;/p&gt;&lt;p&gt;This option is available only for 2D models.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> 
     218             <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Switch on Chain Fitting (parameter reuse) for batch datasets.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> 
    219219            </property> 
    220220            <property name="text"> 
Note: See TracChangeset for help on using the changeset viewer.