Ignore:
Timestamp:
Jun 6, 2017 9:37:52 AM (7 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:
06b0138
Parents:
358b39d
Message:

More polydisp. functionality SASVIEW-601

File:
1 edited

Legend:

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

    r358b39d raca8418  
    4646 
    4747# Mapping between column index and relevant parameter name extension 
     48POLY_COL_NAME = 0 
     49POLY_COL_WIDTH = 1 
     50POLY_COL_MIN = 2 
     51POLY_COL_MAX = 3 
     52POLY_COL_NPTS = 4 
     53POLY_COL_NSIGMAS = 5 
     54POLY_COL_FUNCTION = 6 
    4855POLY_COLUMN_DICT = { 
    49     1: 'width', 
    50     2: 'min', 
    51     3: 'max', 
    52     4: 'npts', 
    53     5: 'nsigmas'} 
     56    POLY_COL_WIDTH:  'width', 
     57    POLY_COL_MIN:    'min', 
     58    POLY_COL_MAX:    'max', 
     59    POLY_COL_NPTS:    'npts', 
     60    POLY_COL_NSIGMAS: 'nsigmas'} 
    5461 
    5562class FittingWidget(QtGui.QWidget, Ui_FittingWidgetUI): 
     
    175182        self.current_shell_displayed = 0 
    176183        self.has_error_column = False 
     184        self.has_poly_error_column = False 
    177185 
    178186        # signal communicator 
     
    251259        self.setTableProperties(self.lstPoly) 
    252260        # Delegates for custom editing and display 
    253         # self.lstPoly.setItemDelegate(PolyViewDelegate(self)) 
     261        self.lstPoly.setItemDelegate(PolyViewDelegate(self)) 
     262        # Polydispersity function combo response 
     263        self.lstPoly.itemDelegate().combo_updated.connect(self.onPolyComboIndexChange) 
    254264 
    255265        # Magnetism model displayed in magnetism list 
     
    368378        self._model_model.itemChanged.connect(self.updateParamsFromModel) 
    369379        self._poly_model.itemChanged.connect(self.onPolyModelChange) 
     380 
    370381        # TODO after the poly_model prototype accepted 
    371382        #self._magnet_model.itemChanged.connect(self.onMagneticModelChange) 
     
    406417        self.parameters_to_fit = None 
    407418        self.has_error_column = False 
     419        self.has_poly_error_column = False 
    408420 
    409421        self.respondToModelStructure(model=model, structure_factor=None) 
     
    488500        # Extract changed value. Assumes proper validation by QValidator/Delegate 
    489501        # TODO: abstract away hardcoded column numbers 
    490         if model_column == 0: 
     502        if model_column == POLY_COL_NAME: 
    491503            # Is the parameter checked for fitting? 
    492504            value = item.checkState() 
     
    499511                    self.parameters_to_fit.remove(parameter_name) 
    500512            return 
    501         elif model_column == 6: 
     513        elif model_column == POLY_COL_FUNCTION: 
    502514            value = item.text() 
    503515            # TODO: Modify Npts/Nsigs based on function choice 
     516        elif model_column in [POLY_COL_MIN, POLY_COL_MAX]: 
     517            try: 
     518                value = float(item.text()) 
     519            except ValueError: 
     520                # Can't be converted properly, bring back the old value and exit 
     521                return 
     522 
     523            property_name = str(self._poly_model.headerData(model_column, 1).toPyObject()).lower() # Value, min, max, etc. 
     524            # print "%s(%s) => %d" % (parameter_name, property_name, value) 
     525            current_details = self.kernel_module.details[parameter_name] 
     526            current_details[model_column-1] = value 
    504527        else: 
    505528            try: 
     
    509532                return 
    510533 
    511         property_name = str(self._poly_model.headerData(model_column, 1).toPyObject()).lower() # Value, min, max, etc. 
    512         # print "%s(%s) => %d" % (parameter_name, property_name, value) 
    513  
    514         # Update the sasmodel 
    515         # PD[ratio] -> width, npts -> npts, nsigs -> nsigmas 
    516         self.kernel_module.setParam(parameter_name + '.' + POLY_COLUMN_DICT[model_column], value) 
     534            property_name = str(self._poly_model.headerData(model_column, 1).toPyObject()).lower() # Value, min, max, etc. 
     535            # print "%s(%s) => %d" % (parameter_name, property_name, value) 
     536 
     537            # Update the sasmodel 
     538            # PD[ratio] -> width, npts -> npts, nsigs -> nsigmas 
     539            self.kernel_module.setParam(parameter_name + '.' + POLY_COLUMN_DICT[model_column], value) 
    517540 
    518541        # Reload the main model - may not be required if no variable is shown in main view 
     
    633656 
    634657        self.chi2 = res.fitness 
    635         param_list = res.param_list 
    636         param_values = res.pvec 
    637         param_stderr = res.stderr 
     658        param_list = res.param_list # ['radius', 'radius.width'] 
     659        param_values = res.pvec     # array([ 0.36221662,  0.0146783 ]) 
     660        param_stderr = res.stderr   # array([ 1.71293015,  1.71294233]) 
    638661        params_and_errors = zip(param_values, param_stderr) 
    639662        param_dict = dict(izip(param_list, params_and_errors)) 
     
    643666        self.updateModelFromList(param_dict) 
    644667 
     668        self.updatePolyModelFromList(param_dict) 
     669 
    645670        # update charts 
    646671        self.onPlot() 
     
    654679        Take func and throw it inside the model row loop 
    655680        """ 
    656         #assert isinstance(func, function) 
    657681        for row_i in xrange(self._model_model.rowCount()): 
    658682            func(row_i) 
     
    715739        self.has_error_column = True 
    716740 
     741    def updatePolyModelFromList(self, param_dict): 
     742        """ 
     743        Update the polydispersity model with new parameters, create the errors column 
     744        """ 
     745        assert isinstance(param_dict, dict) 
     746        if not dict: 
     747            return 
     748 
     749        def updateFittedValues(row_i): 
     750            # Utility function for main model update 
     751            # internal so can use closure for param_dict 
     752            if row_i >= self._poly_model.rowCount(): 
     753                return 
     754            param_name = str(self._poly_model.item(row_i, 0).text()).rsplit()[-1] + '.width' 
     755            if param_name not in param_dict.keys(): 
     756                return 
     757            # modify the param value 
     758            param_repr = GuiUtils.formatNumber(param_dict[param_name][0], high=True) 
     759            self._poly_model.item(row_i, 1).setText(param_repr) 
     760            if self.has_poly_error_column: 
     761                error_repr = GuiUtils.formatNumber(param_dict[param_name][1], high=True) 
     762                self._poly_model.item(row_i, 2).setText(error_repr) 
     763 
     764        def createErrorColumn(row_i): 
     765            # Utility function for error column update 
     766            if row_i >= self._poly_model.rowCount(): 
     767                return 
     768            item = QtGui.QStandardItem() 
     769            for param_name in param_dict.keys(): 
     770                if str(self._poly_model.item(row_i, 0).text()).rsplit()[-1] + '.width' != param_name: 
     771                    continue 
     772                error_repr = GuiUtils.formatNumber(param_dict[param_name][1], high=True) 
     773                item.setText(error_repr) 
     774            error_column.append(item) 
     775 
     776        # block signals temporarily, so we don't end up 
     777        # updating charts with every single model change on the end of fitting 
     778        self._poly_model.blockSignals(True) 
     779        self.iterateOverModel(updateFittedValues) 
     780        self._poly_model.blockSignals(False) 
     781 
     782        return 
     783 
     784        if self.has_poly_error_column: 
     785            return 
     786 
     787        error_column = [] 
     788        self.iterateOverModel(createErrorColumn) 
     789 
     790        # switch off reponse to model change 
     791        self._poly_model.blockSignals(True) 
     792        self._poly_model.insertColumn(2, error_column) 
     793        self._poly_model.blockSignals(False) 
     794        FittingUtilities.addErrorPolyHeadersToModel(self._poly_model) 
     795 
     796        self.has_poly_error_column = True 
     797 
    717798    def onPlot(self): 
    718799        """ 
     
    9211002 
    9221003        # Update the QModel 
    923         new_rows = FittingUtilities.addParametersToModel(self.model_parameters, self.is2D) 
     1004        new_rows = FittingUtilities.addParametersToModel(self.model_parameters, self.kernel_module, self.is2D) 
     1005 
    9241006        for row in new_rows: 
    9251007            self._model_model.appendRow(row) 
     
    9961078        self._model_model.blockSignals(True) 
    9971079        # Convert to proper indices and set requested enablement 
    998         _ = [self._model_model.item(row, 0).setCheckState(status) for row in rows] 
     1080        [self._model_model.item(row, 0).setCheckState(status) for row in rows] 
    9991081        self._model_model.blockSignals(False) 
    10001082 
     
    11151197        calc_thread.addErrback(self.calculateDataFailed) 
    11161198 
    1117     def calculateDataFailed(self): 
     1199    def calculateDataFailed(self, reason): 
    11181200        """ 
    11191201        Thread returned error 
    11201202        """ 
    1121         print "Calculate Data failed." 
     1203        print "Calculate Data failed with ", reason 
    11221204 
    11231205    def complete1D(self, return_data): 
     
    11571239 
    11581240        # Plot residuals if actual data 
    1159         if self.data_is_loaded: 
    1160             residuals_plot = FittingUtilities.plotResiduals(self.data, fitted_data) 
    1161             residuals_plot.id = "Residual " + residuals_plot.id 
    1162             self.createNewIndex(residuals_plot) 
    1163             self.communicate.plotUpdateSignal.emit([residuals_plot]) 
     1241        if not self.data_is_loaded: 
     1242            return 
     1243 
     1244        residuals_plot = FittingUtilities.plotResiduals(self.data, fitted_data) 
     1245        residuals_plot.id = "Residual " + residuals_plot.id 
     1246        self.createNewIndex(residuals_plot) 
     1247        self.communicate.plotUpdateSignal.emit([residuals_plot]) 
    11641248 
    11651249    def calcException(self, etype, value, tb): 
     
    11971281            return 
    11981282        self._poly_model.clear() 
    1199         for row, param in enumerate(self.model_parameters.form_volume_parameters): 
    1200             # Counters should not be included 
    1201             if not param.polydisperse: 
    1202                 continue 
    1203  
    1204             # Values from the sasmodel 
    1205             width = self.kernel_module.getParam(param.name+'.width') 
    1206             npts = self.kernel_module.getParam(param.name+'.npts') 
    1207             nsigs = self.kernel_module.getParam(param.name+'.nsigmas') 
    1208             # Potential multishell params 
    1209             checked_list = ["Distribution of "+param.name, str(width), 
    1210                             str(param.limits[0]), str(param.limits[1]), 
    1211                             str(npts), str(nsigs), ""] 
    1212             FittingUtilities.addCheckedListToModel(self._poly_model, checked_list) 
    1213  
    1214             # All possible polydisp. functions as strings in combobox 
    1215             func = QtGui.QComboBox() 
    1216             func.addItems([str(name_disp) for name_disp in POLYDISPERSITY_MODELS.iterkeys()]) 
    1217             # Default index 
    1218             func.setCurrentIndex(func.findText(DEFAULT_POLYDISP_FUNCTION)) 
    1219             # Index in the view 
    1220             func_index = self.lstPoly.model().index(row, 6) 
    1221             # Set the combobox in cell 
    1222             self.lstPoly.setIndexWidget(func_index, func) 
    1223  
     1283 
     1284        [self.setPolyModelParameters(row, param) for row, param in \ 
     1285            enumerate(self.model_parameters.form_volume_parameters) if param.polydisperse] 
    12241286        FittingUtilities.addPolyHeadersToModel(self._poly_model) 
    12251287 
     1288    def setPolyModelParameters(self, row, param): 
     1289        """ 
     1290        Creates a checked row of for a polydisperse parameter 
     1291        """ 
     1292        # Values from the sasmodel 
     1293        width = self.kernel_module.getParam(param.name+'.width') 
     1294        npts = self.kernel_module.getParam(param.name+'.npts') 
     1295        nsigs = self.kernel_module.getParam(param.name+'.nsigmas') 
     1296        # Potential multishell params 
     1297        checked_list = ["Distribution of "+param.name, str(width), 
     1298                        str(param.limits[0]), str(param.limits[1]), 
     1299                        str(npts), str(nsigs), "gaussian      "] 
     1300        FittingUtilities.addCheckedListToModel(self._poly_model, checked_list) 
     1301 
     1302        # All possible polydisp. functions as strings in combobox 
     1303        func = QtGui.QComboBox() 
     1304        func.addItems([str(name_disp) for name_disp in POLYDISPERSITY_MODELS.iterkeys()]) 
     1305        # Default index 
     1306        func.setCurrentIndex(func.findText(DEFAULT_POLYDISP_FUNCTION)) 
     1307        # Index in the view 
     1308        func_index = self.lstPoly.model().index(row, 6) 
     1309 
     1310    def onPolyComboIndexChange(self, combo_string, row_index): 
     1311        """ 
     1312        Modify polydisp. defaults on function choice 
     1313        """ 
     1314        # get npts/nsigs for current selection 
     1315        param = self.model_parameters.form_volume_parameters[row_index] 
     1316 
     1317        if combo_string == 'array': 
     1318            try: 
     1319                self.loadPolydispArray() 
     1320            except ValueError: 
     1321                # Don't do anything if file lookup failed 
     1322                return 
     1323            # disable the row 
     1324            [self._poly_model.item(row_index, i).setEnabled(False) for i in xrange(6)] 
     1325            return 
     1326 
     1327        # Enable the row in case it was disabled by Array 
     1328        #[self._poly_model.item(row_index, i).setEnabled(True) for i in xrange(6)] 
     1329 
     1330        npts_index = self._poly_model.index(row_index, POLY_COL_NPTS) 
     1331        nsigs_index = self._poly_model.index(row_index, POLY_COL_NSIGMAS) 
     1332 
     1333        npts = POLYDISPERSITY_MODELS[str(combo_string)].default['npts'] 
     1334        nsigs = POLYDISPERSITY_MODELS[str(combo_string)].default['nsigmas'] 
     1335 
     1336        self._poly_model.setData(npts_index, QtCore.QVariant(npts)) 
     1337        self._poly_model.setData(nsigs_index, QtCore.QVariant(nsigs)) 
     1338 
     1339    def loadPolydispArray(self): 
     1340        """ 
     1341        Show the load file dialog and loads requested data into state 
     1342        """ 
     1343        datafile = QtGui.QFileDialog.getOpenFileName( 
     1344            self, "Choose a weight file", "", "All files (*.*)") 
     1345        if not datafile: 
     1346            logging.info("No weight data chosen.") 
     1347            return 
     1348        values = [] 
     1349        weights = [] 
     1350        with open(datafile, 'r') as column_file: 
     1351            column_data = [line.rstrip().split() for line in column_file.readlines()] 
     1352            for line in column_data: 
     1353                try: 
     1354                    values.append(float(line[0])) 
     1355                    weights.append(float(line[1])) 
     1356                except (ValueError, IndexError): 
     1357                    # just pass through if line with bad data 
     1358                    pass 
     1359 
     1360        self.disp_model = POLYDISPERSITY_MODELS['array']() 
     1361        self.disp_model.set_weights(np.array(values), np.array(weights)) 
     1362 
    12261363    def setMagneticModel(self): 
    12271364        """ 
     
    12311368            return 
    12321369        self._magnet_model.clear() 
    1233         for param in self.model_parameters.call_parameters: 
    1234             if param.type != "magnetic": 
    1235                 continue 
    1236             checked_list = [param.name, 
    1237                             str(param.default), 
    1238                             str(param.limits[0]), 
    1239                             str(param.limits[1]), 
    1240                             param.units] 
    1241             FittingUtilities.addCheckedListToModel(self._magnet_model, checked_list) 
    1242  
     1370        [self.addCheckedMagneticListToModel(param, self._magnet_model) for param in \ 
     1371            self.model_parameters.call_parameters if param != 'magnetic'] 
    12431372        FittingUtilities.addHeadersToModel(self._magnet_model) 
     1373 
     1374    def addCheckedMagneticListToModel(self, param, model): 
     1375        """ 
     1376        Wrapper for model update with a subset of magnetic parameters 
     1377        """ 
     1378        checked_list = [param.name, 
     1379                        str(param.default), 
     1380                        str(param.limits[0]), 
     1381                        str(param.limits[1]), 
     1382                        param.units] 
     1383 
     1384        FittingUtilities.addCheckedListToModel(model, checked_list) 
    12441385 
    12451386    def enableStructureFactorControl(self, structure_factor): 
Note: See TracChangeset for help on using the changeset viewer.