Changeset 254199c in sasview for src/sas/qtgui/Perspectives/Fitting
- Timestamp:
- Sep 7, 2018 11:09:07 AM (6 years ago)
- 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. - Location:
- src/sas/qtgui/Perspectives/Fitting
- Files:
-
- 1 added
- 5 edited
Legend:
- Unmodified
- Added
- Removed
-
src/sas/qtgui/Perspectives/Fitting/FittingUtilities.py
r4ea8020 rb69b549 8 8 from sas.qtgui.Plotting.PlotterData import Data1D 9 9 from sas.qtgui.Plotting.PlotterData import Data2D 10 11 from sas.qtgui.Perspectives.Fitting.AssociatedComboBox import AssociatedComboBox 10 12 11 13 model_header_captions = ['Parameter', 'Value', 'Min', 'Max', 'Units'] … … 61 63 return (param_name, param_length) 62 64 63 def addParametersToModel(parameters, kernel_module, is2D): 64 """ 65 Update local ModelModel with sasmodel parameters 65 def 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 93 def 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. 66 98 """ 67 99 multishell_parameters = getIterParams(parameters) … … 72 104 else: 73 105 params = parameters.iq_parameters 74 item = [] 106 107 rows = [] 75 108 for param in params: 76 109 # don't include shell parameters 77 110 if param.name == multishell_param_name: 78 111 continue 112 79 113 # Modify parameter name from <param>[n] to <param>1 80 114 item_name = param.name 81 115 if param in multishell_parameters: 82 116 continue 83 # item_name = replaceShellName(param.name, 1)84 117 85 118 item1 = QtGui.QStandardItem(item_name) 86 119 item1.setCheckable(True) 87 120 item1.setEditable(False) 88 # item_err = QtGui.QStandardItem() 121 89 122 # check for polydisp params 90 123 if param.polydisperse: … … 93 126 item1_1 = QtGui.QStandardItem("Distribution") 94 127 item1_1.setEditable(False) 128 95 129 # Find param in volume_params 96 130 for p in parameters.form_volume_parameters: … … 99 133 width = kernel_module.getParam(p.name+'.width') 100 134 ptype = kernel_module.getParam(p.name+'.type') 101 102 135 item1_2 = QtGui.QStandardItem(str(width)) 103 136 item1_2.setEditable(False) … … 110 143 poly_item.appendRow([item1_1, item1_2, item1_3, item1_4, item1_5]) 111 144 break 145 112 146 # Add the polydisp item as a child 113 147 item1.appendRow([poly_item]) 148 114 149 # Param values 115 150 item2 = QtGui.QStandardItem(str(param.default)) 116 # TODO: the error column.117 # Either add a proxy model or a custom view delegate118 #item_err = QtGui.QStandardItem()119 151 item3 = QtGui.QStandardItem(str(param.limits[0])) 120 152 item4 = QtGui.QStandardItem(str(param.limits[1])) 121 item5 = QtGui.QStandardItem( param.units)153 item5 = QtGui.QStandardItem(str(param.units)) 122 154 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 169 def 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) 131 177 """ 132 178 if is2D: … … 147 193 params_orig = params 148 194 149 item= []195 rows = [] 150 196 for param, param_orig in zip(params, params_orig): 151 197 # Create the top level, checkable item … … 155 201 item1.setCheckable(True) 156 202 item1.setEditable(False) 203 157 204 # Param values 158 205 # TODO: add delegate for validation of cells 159 206 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 166 224 167 225 def markParameterDisabled(model, row): … … 259 317 model.header_tooltips = copy.copy(poly_header_error_tooltips) 260 318 261 def addShellsToModel(parameters, model, index, row_num=None ):319 def addShellsToModel(parameters, model, index, row_num=None, view=None): 262 320 """ 263 321 Find out multishell parameters and update the model with the requested number of them. 264 322 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. 265 324 Returns a list of lists of QStandardItem objects. 266 325 """ … … 285 344 item1_3 = QtGui.QStandardItem(str(p.limits[0])) 286 345 item1_4 = QtGui.QStandardItem(str(p.limits[1])) 287 item1_5 = QtGui.QStandardItem( p.units)346 item1_5 = QtGui.QStandardItem(str(p.units)) 288 347 poly_item.appendRow([item1_1, item1_2, item1_3, item1_4, item1_5]) 289 348 break … … 293 352 item3 = QtGui.QStandardItem(str(par.limits[0])) 294 353 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) 296 358 row = [item1, item2, item3, item4, item5] 297 rows.append(row) 298 359 cbox = createFixedChoiceComboBox(par, row) 360 361 # Always add to the model 299 362 if row_num is None: 300 363 model.appendRow(row) … … 302 365 model.insertRow(row_num, row) 303 366 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) 304 373 305 374 return rows -
src/sas/qtgui/Perspectives/Fitting/FittingWidget.py
r9ba91b7 r254199c 1507 1507 # update charts 1508 1508 self.onPlot() 1509 #self.recalculatePlotData() 1510 1509 1511 1510 1512 # Read only value - we can get away by just printing it here … … 1594 1596 param_repr = GuiUtils.formatNumber(param_dict[param_name][0], high=True) 1595 1597 self._model_model.item(row, 1).setText(param_repr) 1598 self.kernel_module.setParam(param_name, param_dict[param_name][0]) 1596 1599 if self.has_error_column: 1597 1600 error_repr = GuiUtils.formatNumber(param_dict[param_name][1], high=True) … … 1635 1638 poly_item.insertColumn(2, [QtGui.QStandardItem("")]) 1636 1639 1637 # block signals temporarily, so we don't end up1638 # updating charts with every single model change on the end of fitting1639 self._model_model.blockSignals(True)1640 1641 1640 if not self.has_error_column: 1642 1641 # create top-level error column … … 1645 1644 self.iterateOverModel(createErrorColumn) 1646 1645 1647 # we need to enable signals for this, otherwise the final column mysteriously disappears (don't ask, I don't1648 # know)1649 self._model_model.blockSignals(False)1650 1646 self._model_model.insertColumn(2, error_column) 1651 self._model_model.blockSignals(True)1652 1647 1653 1648 FittingUtilities.addErrorHeadersToModel(self._model_model) … … 1658 1653 self.has_error_column = True 1659 1654 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() 1660 1658 self.iterateOverModel(updateFittedValues) 1661 1659 self.iterateOverModel(updatePolyValues) 1662 1663 self._model_model.blockSignals(False) 1660 self._model_model.itemChanged.connect(self.onMainParamsChange) 1664 1661 1665 1662 # Adjust the table cells width. … … 1696 1693 param_repr = GuiUtils.formatNumber(param_dict[param_name][0], high=True) 1697 1694 self._poly_model.item(row_i, 1).setText(param_repr) 1695 self.kernel_module.setParam(param_name, param_dict[param_name][0]) 1698 1696 if self.has_poly_error_column: 1699 1697 error_repr = GuiUtils.formatNumber(param_dict[param_name][1], high=True) 1700 1698 self._poly_model.item(row_i, 2).setText(error_repr) 1701 1702 1699 1703 1700 def createErrorColumn(row_i): … … 1720 1717 # block signals temporarily, so we don't end up 1721 1718 # updating charts with every single model change on the end of fitting 1722 self._poly_model. blockSignals(True)1719 self._poly_model.itemChanged.disconnect() 1723 1720 self.iterateOverPolyModel(updateFittedValues) 1724 self._poly_model. blockSignals(False)1721 self._poly_model.itemChanged.connect(self.onPolyModelChange) 1725 1722 1726 1723 if self.has_poly_error_column: … … 1732 1729 1733 1730 # switch off reponse to model change 1734 self._poly_model.blockSignals(True)1735 1731 self._poly_model.insertColumn(2, error_column) 1736 self._poly_model.blockSignals(False)1737 1732 FittingUtilities.addErrorPolyHeadersToModel(self._poly_model) 1738 1733 … … 1767 1762 param_repr = GuiUtils.formatNumber(param_dict[param_name][0], high=True) 1768 1763 self._magnet_model.item(row, 1).setText(param_repr) 1764 self.kernel_module.setParam(param_name, param_dict[param_name][0]) 1769 1765 if self.has_magnet_error_column: 1770 1766 error_repr = GuiUtils.formatNumber(param_dict[param_name][1], high=True) … … 1786 1782 # block signals temporarily, so we don't end up 1787 1783 # updating charts with every single model change on the end of fitting 1788 self._magnet_model. blockSignals(True)1784 self._magnet_model.itemChanged.disconnect() 1789 1785 self.iterateOverMagnetModel(updateFittedValues) 1790 self._magnet_model. blockSignals(False)1786 self._magnet_model.itemChanged.connect(self.onMagnetModelChange) 1791 1787 1792 1788 if self.has_magnet_error_column: … … 1798 1794 1799 1795 # switch off reponse to model change 1800 self._magnet_model.blockSignals(True)1801 1796 self._magnet_model.insertColumn(2, error_column) 1802 self._magnet_model.blockSignals(False)1803 1797 FittingUtilities.addErrorHeadersToModel(self._magnet_model) 1804 1798 … … 1812 1806 self.cmdPlot.setText("Show Plot") 1813 1807 # Force data recalculation so existing charts are updated 1808 self.showPlot() 1814 1809 self.recalculatePlotData() 1815 self.showPlot()1816 1810 1817 1811 def onSmearingOptionsUpdate(self): … … 2079 2073 self.shell_names = self.shellNamesList() 2080 2074 2081 # Get new rows for QModel2082 new_rows = FittingUtilities.addParametersToModel(self.model_parameters, self.kernel_module, self.is2D)2083 2084 2075 # Add heading row 2085 2076 FittingUtilities.addHeadingRowToModel(self._model_model, model_name) 2086 2077 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) 2090 2085 2091 2086 def fromStructureFactorToQModel(self, structure_factor): … … 2096 2091 return 2097 2092 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 2106 2099 else: 2100 s_kernel = self.models[structure_factor]() 2101 p_kernel = self.kernel_module 2102 2107 2103 p_pars_len = len(p_kernel._model_info.parameters.kernel_parameters) 2108 2104 s_pars_len = len(s_kernel._model_info.parameters.kernel_parameters) … … 2113 2109 2114 2110 # 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 2128 2113 if "radius_effective_mode" in all_param_names: 2129 2114 # Show all parameters … … 2138 2123 s_params = modelinfo.ParameterTable(all_params[p_pars_len:p_pars_len+s_pars_len-1]) 2139 2124 2140 # Get new rows for QModel2141 # Any renamed parameters are stored as data in the relevant item, for later handling2142 new_rows = FittingUtilities.addSimpleParametersToModel(s_params, self.is2D, s_params_orig)2143 2144 # TODO: merge rest of beta approx implementation in2145 # These parameters are not part of P(Q) nor S(Q), but are added only to the product model (e.g. specifying2146 # structure factor calculation mode)2147 # product_params = all_params[p_pars_len+s_pars_len:]2148 2149 2125 # Add heading row 2150 2126 FittingUtilities.addHeadingRowToModel(self._model_model, structure_factor) 2151 2127 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) 2159 2136 2160 2137 def haveParamsToFit(self): … … 2813 2790 self._model_model.removeRows(first_row, remove_rows) 2814 2791 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 2816 2799 self._num_shell_params = len(new_rows) 2817 2818 2800 self.current_shell_displayed = index 2819 2801 -
src/sas/qtgui/Perspectives/Fitting/FittingLogic.py
rdcabba7 r9ba91b7 161 161 Create a new 1D data instance based on fitting results 162 162 """ 163 164 163 return self._create1DPlot(tab_id, return_data['x'], return_data['y'], 165 164 return_data['model'], return_data['data']) … … 212 211 (pq_plot, sq_plot). If either are unavailable, the corresponding plot is None. 213 212 """ 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 228 219 229 220 def computeDataRange(self): -
src/sas/qtgui/Perspectives/Fitting/ModelThread.py
rdcabba7 r9ba91b7 195 195 output[index] = self.model.evalDistribution(self.data.x[index]) 196 196 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 } 215 228 216 229 elapsed = time.time() - self.starttime … … 223 236 source = self.source, unsmeared_output = unsmeared_output, 224 237 unsmeared_data = unsmeared_data, unsmeared_error = unsmeared_error, 225 pq_values = pq_values, sq_values = sq_values)238 intermediate_results = results_eval) 226 239 227 240 if LocalConfig.USING_TWISTED: -
src/sas/qtgui/Perspectives/Fitting/UnitTesting/FittingLogicTest.py
re752ab8 rd6c4987 103 103 data, False, None, 104 104 None, None, None, 105 None , None)105 None) 106 106 107 107 new_plot = self.logic.new1DPlot(return_data=return_data, tab_id=0)
Note: See TracChangeset
for help on using the changeset viewer.