Changeset 254199c in sasview for src/sas/qtgui/Perspectives/Fitting


Ignore:
Timestamp:
Sep 7, 2018 11:09:07 AM (6 years ago)
Author:
Torin Cooper-Bennun <torin.cooper-bennun@…>
Branches:
ESS_GUI, ESS_GUI_batch_fitting, ESS_GUI_bumps_abstraction, ESS_GUI_iss1116, ESS_GUI_iss879, ESS_GUI_opencl, ESS_GUI_ordering, ESS_GUI_sync_sascalc
Children:
5181e9b
Parents:
9ba91b7 (diff), f0365a2e (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the (diff) links above to see all the changes relative to each parent.
Message:

Merge branch 'ESS_GUI' into ESS_GUI_iss1033

Location:
src/sas/qtgui/Perspectives/Fitting
Files:
1 added
5 edited

Legend:

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

    r4ea8020 rb69b549  
    88from sas.qtgui.Plotting.PlotterData import Data1D 
    99from sas.qtgui.Plotting.PlotterData import Data2D 
     10 
     11from sas.qtgui.Perspectives.Fitting.AssociatedComboBox import AssociatedComboBox 
    1012 
    1113model_header_captions = ['Parameter', 'Value', 'Min', 'Max', 'Units'] 
     
    6163    return (param_name, param_length) 
    6264 
    63 def addParametersToModel(parameters, kernel_module, is2D): 
    64     """ 
    65     Update local ModelModel with sasmodel parameters 
     65def createFixedChoiceComboBox(param, item_row): 
     66    """ 
     67    Determines whether param is a fixed-choice parameter, modifies items in item_row appropriately and returns a combo 
     68    box containing the fixed choices. Returns None if param is not fixed-choice. 
     69     
     70    item_row is a list of QStandardItem objects for insertion into the parameter table.  
     71    """ 
     72 
     73    # Determine whether this is a fixed-choice parameter. There are lots of conditionals, simply because the 
     74    # implementation is not yet concrete; there are several possible indicators that the parameter is fixed-choice. 
     75    # TODO: (when the sasmodels implementation is concrete, clean this up) 
     76    choices = None 
     77    if isinstance(param.choices, (list, tuple)) and len(param.choices) > 0: 
     78        # The choices property is concrete in sasmodels, probably will use this 
     79        choices = param.choices 
     80    elif isinstance(param.units, (list, tuple)): 
     81        choices = [str(x) for x in param.units] 
     82 
     83    cbox = None 
     84    if choices is not None: 
     85        # Use combo box for input, if it is fixed-choice 
     86        cbox = AssociatedComboBox(item_row[1], idx_as_value=True) 
     87        cbox.addItems(choices) 
     88        item_row[2].setEditable(False) 
     89        item_row[3].setEditable(False) 
     90 
     91    return cbox 
     92 
     93def addParametersToModel(parameters, kernel_module, is2D, model=None, view=None): 
     94    """ 
     95    Update local ModelModel with sasmodel parameters. 
     96    Actually appends to model, if model and view params are not None. 
     97    Always returns list of lists of QStandardItems. 
    6698    """ 
    6799    multishell_parameters = getIterParams(parameters) 
     
    72104    else: 
    73105        params = parameters.iq_parameters 
    74     item = [] 
     106 
     107    rows = [] 
    75108    for param in params: 
    76109        # don't include shell parameters 
    77110        if param.name == multishell_param_name: 
    78111            continue 
     112 
    79113        # Modify parameter name from <param>[n] to <param>1 
    80114        item_name = param.name 
    81115        if param in multishell_parameters: 
    82116            continue 
    83         #    item_name = replaceShellName(param.name, 1) 
    84117 
    85118        item1 = QtGui.QStandardItem(item_name) 
    86119        item1.setCheckable(True) 
    87120        item1.setEditable(False) 
    88         # item_err = QtGui.QStandardItem() 
     121 
    89122        # check for polydisp params 
    90123        if param.polydisperse: 
     
    93126            item1_1 = QtGui.QStandardItem("Distribution") 
    94127            item1_1.setEditable(False) 
     128 
    95129            # Find param in volume_params 
    96130            for p in parameters.form_volume_parameters: 
     
    99133                width = kernel_module.getParam(p.name+'.width') 
    100134                ptype = kernel_module.getParam(p.name+'.type') 
    101  
    102135                item1_2 = QtGui.QStandardItem(str(width)) 
    103136                item1_2.setEditable(False) 
     
    110143                poly_item.appendRow([item1_1, item1_2, item1_3, item1_4, item1_5]) 
    111144                break 
     145 
    112146            # Add the polydisp item as a child 
    113147            item1.appendRow([poly_item]) 
     148 
    114149        # Param values 
    115150        item2 = QtGui.QStandardItem(str(param.default)) 
    116         # TODO: the error column. 
    117         # Either add a proxy model or a custom view delegate 
    118         #item_err = QtGui.QStandardItem() 
    119151        item3 = QtGui.QStandardItem(str(param.limits[0])) 
    120152        item4 = QtGui.QStandardItem(str(param.limits[1])) 
    121         item5 = QtGui.QStandardItem(param.units) 
     153        item5 = QtGui.QStandardItem(str(param.units)) 
    122154        item5.setEditable(False) 
    123         item.append([item1, item2, item3, item4, item5]) 
    124     return item 
    125  
    126 def addSimpleParametersToModel(parameters, is2D, parameters_original=None): 
    127     """ 
    128     Update local ModelModel with sasmodel parameters 
    129     parameters_original: list of parameters before any tagging on their IDs, e.g. for product model 
    130     (so that those are the display names; see below) 
     155 
     156        # Check if fixed-choice (returns combobox, if so, also makes some items uneditable) 
     157        row = [item1, item2, item3, item4, item5] 
     158        cbox = createFixedChoiceComboBox(param, row) 
     159 
     160        # Append to the model and use the combobox, if required 
     161        if None not in (model, view): 
     162            model.appendRow(row) 
     163            if cbox: 
     164                view.setIndexWidget(item2.index(), cbox) 
     165        rows.append(row) 
     166 
     167    return rows 
     168 
     169def addSimpleParametersToModel(parameters, is2D, parameters_original=None, model=None, view=None): 
     170    """ 
     171    Update local ModelModel with sasmodel parameters (non-dispersed, non-magnetic) 
     172    Actually appends to model, if model and view params are not None. 
     173    Always returns list of lists of QStandardItems. 
     174 
     175    parameters_original: list of parameters before any tagging on their IDs, e.g. for product model (so that those are 
     176    the display names; see below) 
    131177    """ 
    132178    if is2D: 
     
    147193        params_orig = params 
    148194 
    149     item = [] 
     195    rows = [] 
    150196    for param, param_orig in zip(params, params_orig): 
    151197        # Create the top level, checkable item 
     
    155201        item1.setCheckable(True) 
    156202        item1.setEditable(False) 
     203 
    157204        # Param values 
    158205        # TODO: add delegate for validation of cells 
    159206        item2 = QtGui.QStandardItem(str(param.default)) 
    160         item4 = QtGui.QStandardItem(str(param.limits[0])) 
    161         item5 = QtGui.QStandardItem(str(param.limits[1])) 
    162         item6 = QtGui.QStandardItem(str(param.units)) 
    163         item6.setEditable(False) 
    164         item.append([item1, item2, item4, item5, item6]) 
    165     return item 
     207        item3 = QtGui.QStandardItem(str(param.limits[0])) 
     208        item4 = QtGui.QStandardItem(str(param.limits[1])) 
     209        item5 = QtGui.QStandardItem(str(param.units)) 
     210        item5.setEditable(False) 
     211 
     212        # Check if fixed-choice (returns combobox, if so, also makes some items uneditable) 
     213        row = [item1, item2, item3, item4, item5] 
     214        cbox = createFixedChoiceComboBox(param, row) 
     215 
     216        # Append to the model and use the combobox, if required 
     217        if None not in (model, view): 
     218            model.appendRow(row) 
     219            if cbox: 
     220                view.setIndexWidget(item2.index(), cbox) 
     221        rows.append(row) 
     222 
     223    return rows 
    166224 
    167225def markParameterDisabled(model, row): 
     
    259317    model.header_tooltips = copy.copy(poly_header_error_tooltips) 
    260318 
    261 def addShellsToModel(parameters, model, index, row_num=None): 
     319def addShellsToModel(parameters, model, index, row_num=None, view=None): 
    262320    """ 
    263321    Find out multishell parameters and update the model with the requested number of them. 
    264322    Inserts them after the row at row_num, if not None; otherwise, appends to end. 
     323    If view param is not None, supports fixed-choice params. 
    265324    Returns a list of lists of QStandardItem objects. 
    266325    """ 
     
    285344                    item1_3 = QtGui.QStandardItem(str(p.limits[0])) 
    286345                    item1_4 = QtGui.QStandardItem(str(p.limits[1])) 
    287                     item1_5 = QtGui.QStandardItem(p.units) 
     346                    item1_5 = QtGui.QStandardItem(str(p.units)) 
    288347                    poly_item.appendRow([item1_1, item1_2, item1_3, item1_4, item1_5]) 
    289348                    break 
     
    293352            item3 = QtGui.QStandardItem(str(par.limits[0])) 
    294353            item4 = QtGui.QStandardItem(str(par.limits[1])) 
    295             item5 = QtGui.QStandardItem(par.units) 
     354            item5 = QtGui.QStandardItem(str(par.units)) 
     355            item5.setEditable(False) 
     356 
     357            # Check if fixed-choice (returns combobox, if so, also makes some items uneditable) 
    296358            row = [item1, item2, item3, item4, item5] 
    297             rows.append(row) 
    298  
     359            cbox = createFixedChoiceComboBox(par, row) 
     360 
     361            # Always add to the model 
    299362            if row_num is None: 
    300363                model.appendRow(row) 
     
    302365                model.insertRow(row_num, row) 
    303366                row_num += 1 
     367 
     368            # Apply combobox if required 
     369            if None not in (view, cbox): 
     370                view.setIndexWidget(item2.index(), cbox) 
     371 
     372            rows.append(row) 
    304373 
    305374    return rows 
  • src/sas/qtgui/Perspectives/Fitting/FittingWidget.py

    r9ba91b7 r254199c  
    15071507        # update charts 
    15081508        self.onPlot() 
     1509        #self.recalculatePlotData() 
     1510 
    15091511 
    15101512        # Read only value - we can get away by just printing it here 
     
    15941596            param_repr = GuiUtils.formatNumber(param_dict[param_name][0], high=True) 
    15951597            self._model_model.item(row, 1).setText(param_repr) 
     1598            self.kernel_module.setParam(param_name, param_dict[param_name][0]) 
    15961599            if self.has_error_column: 
    15971600                error_repr = GuiUtils.formatNumber(param_dict[param_name][1], high=True) 
     
    16351638            poly_item.insertColumn(2, [QtGui.QStandardItem("")]) 
    16361639 
    1637         # block signals temporarily, so we don't end up 
    1638         # updating charts with every single model change on the end of fitting 
    1639         self._model_model.blockSignals(True) 
    1640  
    16411640        if not self.has_error_column: 
    16421641            # create top-level error column 
     
    16451644            self.iterateOverModel(createErrorColumn) 
    16461645 
    1647             # we need to enable signals for this, otherwise the final column mysteriously disappears (don't ask, I don't 
    1648             # know) 
    1649             self._model_model.blockSignals(False) 
    16501646            self._model_model.insertColumn(2, error_column) 
    1651             self._model_model.blockSignals(True) 
    16521647 
    16531648            FittingUtilities.addErrorHeadersToModel(self._model_model) 
     
    16581653            self.has_error_column = True 
    16591654 
     1655        # block signals temporarily, so we don't end up 
     1656        # updating charts with every single model change on the end of fitting 
     1657        self._model_model.itemChanged.disconnect() 
    16601658        self.iterateOverModel(updateFittedValues) 
    16611659        self.iterateOverModel(updatePolyValues) 
    1662  
    1663         self._model_model.blockSignals(False) 
     1660        self._model_model.itemChanged.connect(self.onMainParamsChange) 
    16641661 
    16651662        # Adjust the table cells width. 
     
    16961693            param_repr = GuiUtils.formatNumber(param_dict[param_name][0], high=True) 
    16971694            self._poly_model.item(row_i, 1).setText(param_repr) 
     1695            self.kernel_module.setParam(param_name, param_dict[param_name][0]) 
    16981696            if self.has_poly_error_column: 
    16991697                error_repr = GuiUtils.formatNumber(param_dict[param_name][1], high=True) 
    17001698                self._poly_model.item(row_i, 2).setText(error_repr) 
    1701  
    17021699 
    17031700        def createErrorColumn(row_i): 
     
    17201717        # block signals temporarily, so we don't end up 
    17211718        # updating charts with every single model change on the end of fitting 
    1722         self._poly_model.blockSignals(True) 
     1719        self._poly_model.itemChanged.disconnect() 
    17231720        self.iterateOverPolyModel(updateFittedValues) 
    1724         self._poly_model.blockSignals(False) 
     1721        self._poly_model.itemChanged.connect(self.onPolyModelChange) 
    17251722 
    17261723        if self.has_poly_error_column: 
     
    17321729 
    17331730        # switch off reponse to model change 
    1734         self._poly_model.blockSignals(True) 
    17351731        self._poly_model.insertColumn(2, error_column) 
    1736         self._poly_model.blockSignals(False) 
    17371732        FittingUtilities.addErrorPolyHeadersToModel(self._poly_model) 
    17381733 
     
    17671762            param_repr = GuiUtils.formatNumber(param_dict[param_name][0], high=True) 
    17681763            self._magnet_model.item(row, 1).setText(param_repr) 
     1764            self.kernel_module.setParam(param_name, param_dict[param_name][0]) 
    17691765            if self.has_magnet_error_column: 
    17701766                error_repr = GuiUtils.formatNumber(param_dict[param_name][1], high=True) 
     
    17861782        # block signals temporarily, so we don't end up 
    17871783        # updating charts with every single model change on the end of fitting 
    1788         self._magnet_model.blockSignals(True) 
     1784        self._magnet_model.itemChanged.disconnect() 
    17891785        self.iterateOverMagnetModel(updateFittedValues) 
    1790         self._magnet_model.blockSignals(False) 
     1786        self._magnet_model.itemChanged.connect(self.onMagnetModelChange) 
    17911787 
    17921788        if self.has_magnet_error_column: 
     
    17981794 
    17991795        # switch off reponse to model change 
    1800         self._magnet_model.blockSignals(True) 
    18011796        self._magnet_model.insertColumn(2, error_column) 
    1802         self._magnet_model.blockSignals(False) 
    18031797        FittingUtilities.addErrorHeadersToModel(self._magnet_model) 
    18041798 
     
    18121806        self.cmdPlot.setText("Show Plot") 
    18131807        # Force data recalculation so existing charts are updated 
     1808        self.showPlot() 
    18141809        self.recalculatePlotData() 
    1815         self.showPlot() 
    18161810 
    18171811    def onSmearingOptionsUpdate(self): 
     
    20792073        self.shell_names = self.shellNamesList() 
    20802074 
    2081         # Get new rows for QModel 
    2082         new_rows = FittingUtilities.addParametersToModel(self.model_parameters, self.kernel_module, self.is2D) 
    2083  
    20842075        # Add heading row 
    20852076        FittingUtilities.addHeadingRowToModel(self._model_model, model_name) 
    20862077 
    2087         # Update QModel 
    2088         for row in new_rows: 
    2089             self._model_model.appendRow(row) 
     2078        # Update the QModel 
     2079        FittingUtilities.addParametersToModel( 
     2080                self.model_parameters, 
     2081                self.kernel_module, 
     2082                self.is2D, 
     2083                self._model_model, 
     2084                self.lstParams) 
    20902085 
    20912086    def fromStructureFactorToQModel(self, structure_factor): 
     
    20962091            return 
    20972092 
    2098         s_kernel = self.models[structure_factor]() 
    2099         p_kernel = self.kernel_module 
    2100  
    2101         if p_kernel is None: 
    2102             # Not a product model, just S(Q) 
    2103             self.kernel_module = s_kernel 
    2104             params = modelinfo.ParameterTable(self.kernel_module._model_info.parameters.kernel_parameters) 
    2105             new_rows = FittingUtilities.addSimpleParametersToModel(params, self.is2D) 
     2093        if self.kernel_module is None: 
     2094            # Structure factor is the only selected model; build it and show all its params 
     2095            self.kernel_module = self.models[structure_factor]() 
     2096            s_params = self.kernel_module._model_info.parameters 
     2097            s_params_orig = s_params 
     2098 
    21062099        else: 
     2100            s_kernel = self.models[structure_factor]() 
     2101            p_kernel = self.kernel_module 
     2102 
    21072103            p_pars_len = len(p_kernel._model_info.parameters.kernel_parameters) 
    21082104            s_pars_len = len(s_kernel._model_info.parameters.kernel_parameters) 
     
    21132109 
    21142110            # S(Q) params from the product model are not necessarily the same as those from the S(Q) model; any 
    2115             # conflicting names with P(Q) params will cause a rename; we also lose radius_effective (for now...) 
    2116  
    2117             # TODO: merge rest of beta approx implementation in 
    2118             # This is to ensure compatibility when we merge beta approx support in...! 
    2119  
    2120             # radius_effective is always s_params[0] 
    2121  
    2122             # if radius_effective_mode is in all_params, then all_params contains radius_effective and we want to 
    2123             # keep it in the model 
    2124  
    2125             # if radius_effective_mode is NOT in all_params, then radius_effective should NOT be kept, because the user 
    2126             # cannot specify it themselves; but, make sure we only remove it if it's actually there in the first place 
    2127             # (sasmodels master removes it already) 
     2111            # conflicting names with P(Q) params will cause a rename 
     2112 
    21282113            if "radius_effective_mode" in all_param_names: 
    21292114                # Show all parameters 
     
    21382123                    s_params = modelinfo.ParameterTable(all_params[p_pars_len:p_pars_len+s_pars_len-1]) 
    21392124 
    2140             # Get new rows for QModel 
    2141             # Any renamed parameters are stored as data in the relevant item, for later handling 
    2142             new_rows = FittingUtilities.addSimpleParametersToModel(s_params, self.is2D, s_params_orig) 
    2143  
    2144             # TODO: merge rest of beta approx implementation in 
    2145             # These parameters are not part of P(Q) nor S(Q), but are added only to the product model (e.g. specifying 
    2146             # structure factor calculation mode) 
    2147             # product_params = all_params[p_pars_len+s_pars_len:] 
    2148  
    21492125        # Add heading row 
    21502126        FittingUtilities.addHeadingRowToModel(self._model_model, structure_factor) 
    21512127 
    2152         # Update QModel 
    2153         for row in new_rows: 
    2154             self._model_model.appendRow(row) 
    2155             # disable fitting of parameters not listed in self.kernel_module (probably radius_effective) 
    2156             # if row[0].text() not in self.kernel_module.params.keys(): 
    2157             #     row_num = self._model_model.rowCount() - 1 
    2158             #     FittingUtilities.markParameterDisabled(self._model_model, row_num) 
     2128        # Get new rows for QModel 
     2129        # Any renamed parameters are stored as data in the relevant item, for later handling 
     2130        FittingUtilities.addSimpleParametersToModel( 
     2131                s_params, 
     2132                self.is2D, 
     2133                s_params_orig, 
     2134                self._model_model, 
     2135                self.lstParams) 
    21592136 
    21602137    def haveParamsToFit(self): 
     
    28132790            self._model_model.removeRows(first_row, remove_rows) 
    28142791 
    2815         new_rows = FittingUtilities.addShellsToModel(self.model_parameters, self._model_model, index, first_row) 
     2792        new_rows = FittingUtilities.addShellsToModel( 
     2793                self.model_parameters, 
     2794                self._model_model, 
     2795                index, 
     2796                first_row, 
     2797                self.lstParams) 
     2798 
    28162799        self._num_shell_params = len(new_rows) 
    2817  
    28182800        self.current_shell_displayed = index 
    28192801 
  • src/sas/qtgui/Perspectives/Fitting/FittingLogic.py

    rdcabba7 r9ba91b7  
    161161        Create a new 1D data instance based on fitting results 
    162162        """ 
    163  
    164163        return self._create1DPlot(tab_id, return_data['x'], return_data['y'], 
    165164                                  return_data['model'], return_data['data']) 
     
    212211        (pq_plot, sq_plot). If either are unavailable, the corresponding plot is None. 
    213212        """ 
    214  
    215         pq_plot = None 
    216         sq_plot = None 
    217  
    218         if return_data.get('pq_values', None) is not None: 
    219             pq_plot = self._create1DPlot(tab_id, return_data['x'], 
    220                     return_data['pq_values'], return_data['model'], 
    221                     return_data['data'], component="P(Q)") 
    222         if return_data.get('sq_values', None) is not None: 
    223             sq_plot = self._create1DPlot(tab_id, return_data['x'], 
    224                     return_data['sq_values'], return_data['model'], 
    225                     return_data['data'], component="S(Q)") 
    226  
    227         return pq_plot, sq_plot 
     213        plots = [] 
     214        for name, result in return_data['intermediate_results'].items(): 
     215            plots.append(self._create1DPlot(tab_id, return_data['x'], result, 
     216                         return_data['model'], return_data['data'], 
     217                         component=name)) 
     218        return plots 
    228219 
    229220    def computeDataRange(self): 
  • src/sas/qtgui/Perspectives/Fitting/ModelThread.py

    rdcabba7 r9ba91b7  
    195195            output[index] = self.model.evalDistribution(self.data.x[index]) 
    196196 
    197         sq_values = None 
    198         pq_values = None 
    199         s_model = None 
    200         p_model = None 
    201         if isinstance(self.model, MultiplicationModel): 
    202             s_model = self.model.s_model 
    203             p_model = self.model.p_model 
    204         elif hasattr(self.model, "calc_composition_models"): 
    205             results = self.model.calc_composition_models(self.data.x[index]) 
    206             if results is not None: 
    207                 pq_values, sq_values = results 
    208  
    209         if pq_values is None or sq_values is None: 
    210             if p_model is not None and s_model is not None: 
    211                 sq_values = numpy.zeros((len(self.data.x))) 
    212                 pq_values = numpy.zeros((len(self.data.x))) 
    213                 sq_values[index] = s_model.evalDistribution(self.data.x[index]) 
    214                 pq_values[index] = p_model.evalDistribution(self.data.x[index]) 
     197        results_eval = {} 
     198        intermediate_results = getattr(self.model, "_intermediate_results", None) 
     199        if callable(intermediate_results): 
     200            # support for future sasmodels (beta approx support) - it returns a dict of intermediate results, keyed by 
     201            # name 
     202            results_eval = intermediate_results() 
     203        else: 
     204            sq_values = None 
     205            pq_values = None 
     206            s_model = None 
     207            p_model = None 
     208            if isinstance(self.model, MultiplicationModel): 
     209                s_model = self.model.s_model 
     210                p_model = self.model.p_model 
     211            elif hasattr(self.model, "calc_composition_models"): 
     212                results = self.model.calc_composition_models(self.data.x[index]) 
     213                if results is not None: 
     214                    pq_values, sq_values = results 
     215 
     216            if pq_values is None or sq_values is None: 
     217                if p_model is not None and s_model is not None: 
     218                    sq_values = numpy.zeros((len(self.data.x))) 
     219                    pq_values = numpy.zeros((len(self.data.x))) 
     220                    sq_values[index] = s_model.evalDistribution(self.data.x[index]) 
     221                    pq_values[index] = p_model.evalDistribution(self.data.x[index]) 
     222 
     223            if pq_values is not None and sq_values is not None: 
     224                results_eval = { 
     225                    "P(Q)": pq_values, 
     226                    "S(Q)": sq_values 
     227                } 
    215228 
    216229        elapsed = time.time() - self.starttime 
     
    223236            source = self.source, unsmeared_output = unsmeared_output, 
    224237            unsmeared_data = unsmeared_data, unsmeared_error = unsmeared_error, 
    225             pq_values = pq_values, sq_values = sq_values) 
     238            intermediate_results = results_eval) 
    226239 
    227240        if LocalConfig.USING_TWISTED: 
  • src/sas/qtgui/Perspectives/Fitting/UnitTesting/FittingLogicTest.py

    re752ab8 rd6c4987  
    103103                       data, False, None, 
    104104                       None, None, None, 
    105                        None, None) 
     105                       None) 
    106106 
    107107        new_plot = self.logic.new1DPlot(return_data=return_data, tab_id=0) 
Note: See TracChangeset for help on using the changeset viewer.