Changeset 4ea8020 in sasview for src/sas/qtgui/Perspectives/Fitting/FittingWidget.py
- Timestamp:
- Sep 7, 2018 7:40:56 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:
- c0de493
- Parents:
- 0109f2a (diff), dcabba7 (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. - git-author:
- Torin Cooper-Bennun <40573959+tcbennun@…> (09/07/18 07:40:56)
- git-committer:
- GitHub <noreply@…> (09/07/18 07:40:56)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
src/sas/qtgui/Perspectives/Fitting/FittingWidget.py
r0109f2a r4ea8020 49 49 50 50 51 52 51 TAB_MAGNETISM = 4 53 52 TAB_POLY = 3 … … 61 60 62 61 logger = logging.getLogger(__name__) 63 64 62 65 63 class ToolTippedItemModel(QtGui.QStandardItemModel): … … 93 91 fittingFinishedSignal = QtCore.pyqtSignal(tuple) 94 92 batchFittingFinishedSignal = QtCore.pyqtSignal(tuple) 95 Calc1DFinishedSignal = QtCore.pyqtSignal( tuple)96 Calc2DFinishedSignal = QtCore.pyqtSignal( tuple)93 Calc1DFinishedSignal = QtCore.pyqtSignal(dict) 94 Calc2DFinishedSignal = QtCore.pyqtSignal(dict) 97 95 98 96 def __init__(self, parent=None, data=None, tab_id=1): … … 251 249 self.kernel_module_copy = None 252 250 251 # dictionaries of current params 252 self.poly_params = {} 253 self.magnet_params = {} 254 253 255 # Page id for fitting 254 256 # To keep with previous SasView values, use 200 as the start offset … … 267 269 self.has_poly_error_column = False 268 270 self.has_magnet_error_column = False 271 272 # If the widget generated theory item, save it 273 self.theory_item = None 269 274 270 275 # signal communicator … … 390 395 # Tag along functionality 391 396 self.label.setText("Data loaded from: ") 392 self.lblFilename.setText(self.logic.data.filename) 397 if self.logic.data.filename: 398 self.lblFilename.setText(self.logic.data.filename) 399 else: 400 self.lblFilename.setText(self.logic.data.name) 393 401 self.updateQRange() 394 402 # Switch off Data2D control … … 522 530 523 531 # Signals from separate tabs asking for replot 524 self.options_widget.plot_signal.connect(self.onOptionsUpdate)525 532 self.options_widget.plot_signal.connect(self.onOptionsUpdate) 526 533 … … 964 971 model = self.cbModel.currentText() 965 972 966 # empty combobox forced to be read 973 # Assure the control is active 974 if not self.cbModel.isEnabled(): 975 return 976 # Empty combobox forced to be read 967 977 if not model: 968 978 return 969 # Reset structure factor970 self.cbStructureFactor.setCurrentIndex(0)971 979 972 980 # Reset parameters to fit … … 975 983 self.has_poly_error_column = False 976 984 977 self.respondToModelStructure(model=model, structure_factor=None) 985 structure = None 986 if self.cbStructureFactor.isEnabled(): 987 structure = str(self.cbStructureFactor.currentText()) 988 self.respondToModelStructure(model=model, structure_factor=structure) 978 989 979 990 def onSelectBatchFilename(self, data_index): … … 1118 1129 self.disableModelCombo() 1119 1130 self.enableStructureCombo() 1131 # set the index to 0 1132 self.cbStructureFactor.setCurrentIndex(0) 1133 self.model_parameters = None 1120 1134 self._model_model.clear() 1121 1135 return … … 1142 1156 model_row = item.row() 1143 1157 name_index = self._poly_model.index(model_row, 0) 1144 parameter_name = str(name_index.data()) .lower()# "distribution of sld" etc.1145 if " distribution of" in parameter_name:1158 parameter_name = str(name_index.data()) # "distribution of sld" etc. 1159 if "istribution of" in parameter_name: 1146 1160 # just the last word 1147 1161 parameter_name = parameter_name.rsplit()[-1] … … 1188 1202 # Update the sasmodel 1189 1203 # PD[ratio] -> width, npts -> npts, nsigs -> nsigmas 1190 self.kernel_module.setParam(parameter_name + '.' + delegate.columnDict()[model_column], value) 1204 #self.kernel_module.setParam(parameter_name + '.' + delegate.columnDict()[model_column], value) 1205 key = parameter_name + '.' + delegate.columnDict()[model_column] 1206 self.poly_params[key] = value 1191 1207 1192 1208 # Update plot … … 1197 1213 row = self.getRowFromName(parameter_name) 1198 1214 param_item = self._model_model.item(row) 1215 self._model_model.blockSignals(True) 1199 1216 param_item.child(0).child(0, model_column).setText(item.text()) 1217 self._model_model.blockSignals(False) 1200 1218 1201 1219 def onMagnetModelChange(self, item): … … 1226 1244 # Unparsable field 1227 1245 return 1228 1229 property_index = self._magnet_model.headerData(1, model_column)-1 # Value, min, max, etc. 1230 1231 # Update the parameter value - note: this supports +/-inf as well 1232 self.kernel_module.params[parameter_name] = value 1233 1234 # min/max to be changed in self.kernel_module.details[parameter_name] = ['Ang', 0.0, inf] 1235 self.kernel_module.details[parameter_name][property_index] = value 1236 1237 # Force the chart update when actual parameters changed 1238 if model_column == 1: 1246 delegate = self.lstMagnetic.itemDelegate() 1247 1248 if model_column > 1: 1249 if model_column == delegate.mag_min: 1250 pos = 1 1251 elif model_column == delegate.mag_max: 1252 pos = 2 1253 elif model_column == delegate.mag_unit: 1254 pos = 0 1255 else: 1256 raise AttributeError("Wrong column in magnetism table.") 1257 # min/max to be changed in self.kernel_module.details[parameter_name] = ['Ang', 0.0, inf] 1258 self.kernel_module.details[parameter_name][pos] = value 1259 else: 1260 self.magnet_params[parameter_name] = value 1261 #self.kernel_module.setParam(parameter_name) = value 1262 # Force the chart update when actual parameters changed 1239 1263 self.recalculatePlotData() 1240 1264 … … 1497 1521 # Data going in 1498 1522 data = self.logic.data 1499 model = self.kernel_module1523 model = copy.deepcopy(self.kernel_module) 1500 1524 qmin = self.q_range_min 1501 1525 qmax = self.q_range_max 1526 # add polydisperse/magnet parameters if asked 1527 self.updateKernelModelWithExtraParams(model) 1502 1528 1503 1529 params_to_fit = self.main_params_to_fit … … 1509 1535 raise ValueError('Fitting requires at least one parameter to optimize.') 1510 1536 1511 # Potential smearing added1512 # Remember that smearing_min/max can be None ->1513 # deal with it until Python gets discriminated unions1514 self.addWeightingToData(data)1515 1516 1537 # Get the constraints. 1517 1538 constraints = self.getComplexConstraintsForModel() … … 1530 1551 data = GuiUtils.dataFromItem(fit_index) 1531 1552 # Potential weights added directly to data 1532 self.addWeightingToData(data)1553 weighted_data = self.addWeightingToData(data) 1533 1554 try: 1534 fitter_single.set_model(model, fit_id, params_to_fit, data= data,1555 fitter_single.set_model(model, fit_id, params_to_fit, data=weighted_data, 1535 1556 constraints=constraints) 1536 1557 except ValueError as ex: 1537 1558 raise ValueError("Setting model parameters failed with: %s" % ex) 1538 1559 1539 qmin, qmax, _ = self.logic.computeRangeFromData( data)1540 fitter_single.set_data(data= data, id=fit_id, smearer=smearer, qmin=qmin,1560 qmin, qmax, _ = self.logic.computeRangeFromData(weighted_data) 1561 fitter_single.set_data(data=weighted_data, id=fit_id, smearer=smearer, qmin=qmin, 1541 1562 qmax=qmax) 1542 1563 fitter_single.select_problem_for_fit(id=fit_id, value=1) … … 1929 1950 Adds weighting contribution to fitting data 1930 1951 """ 1952 new_data = copy.deepcopy(data) 1931 1953 # Send original data for weighting 1932 1954 weight = FittingUtilities.getWeight(data=data, is2d=self.is2D, flag=self.weighting) 1933 1955 if self.is2D: 1934 data.err_data = weight1956 new_data.err_data = weight 1935 1957 else: 1936 data.dy = weight 1937 pass 1958 new_data.dy = weight 1959 1960 return new_data 1938 1961 1939 1962 def updateQRange(self): … … 1970 1993 self.addExtraShells() 1971 1994 1972 if structure_factor not in (None, "None"): 1973 # add S(Q) 1995 # Allow the SF combobox visibility for the given sasmodel 1996 self.enableStructureFactorControl(structure_factor) 1997 1998 # Add S(Q) 1999 if self.cbStructureFactor.isEnabled(): 2000 structure_factor = self.cbStructureFactor.currentText() 1974 2001 self.fromStructureFactorToQModel(structure_factor) 1975 else:1976 # enable selection of S(Q)1977 self.enableStructureFactorControl(structure_factor)1978 2002 1979 2003 # Add polydispersity to the model 2004 self.poly_params = {} 1980 2005 self.setPolyModel() 1981 2006 # Add magnetic parameters to the model 2007 self.magnet_params = {} 1982 2008 self.setMagneticModel() 1983 2009 … … 2005 2031 """ 2006 2032 name = model_name 2033 kernel_module = None 2007 2034 if self.cbCategory.currentText() == CATEGORY_CUSTOM: 2008 2035 # custom kernel load requires full path … … 2010 2037 try: 2011 2038 kernel_module = generate.load_kernel_module(name) 2012 except ModuleNotFoundError: 2013 # maybe it's a recategorised custom model? 2014 name = os.path.join(ModelUtilities.find_plugins_dir(), model_name+".py") 2015 # If this rises, it's a valid problem. 2016 kernel_module = generate.load_kernel_module(name) 2039 except ModuleNotFoundError as ex: 2040 pass 2041 2042 if kernel_module is None: 2043 # mismatch between "name" attribute and actual filename. 2044 curr_model = self.models[model_name] 2045 name, _ = os.path.splitext(os.path.basename(curr_model.filename)) 2046 try: 2047 kernel_module = generate.load_kernel_module(name) 2048 except ModuleNotFoundError as ex: 2049 logging.error("Can't find the model "+ str(ex)) 2050 return 2017 2051 2018 2052 if hasattr(kernel_module, 'parameters'): … … 2059 2093 Setting model parameters into QStandardItemModel based on selected _structure factor_ 2060 2094 """ 2095 if structure_factor is None or structure_factor=="None": 2096 return 2097 2061 2098 s_kernel = self.models[structure_factor]() 2062 2099 p_kernel = self.kernel_module … … 2257 2294 name = self.nameFromData(fitted_data) 2258 2295 # Notify the GUI manager so it can create the theory model in DataExplorer 2259 new_item = GuiUtils.createModelItemWithPlot(fitted_data, name=name)2260 self.communicate.updateTheoryFromPerspectiveSignal.emit( new_item)2296 self.theory_item = GuiUtils.createModelItemWithPlot(fitted_data, name=name) 2297 self.communicate.updateTheoryFromPerspectiveSignal.emit(self.theory_item) 2261 2298 2262 2299 def nameFromData(self, fitted_data): … … 2281 2318 return self.completed1D if isinstance(self.data, Data1D) else self.completed2D 2282 2319 2320 def updateKernelModelWithExtraParams(self, model=None): 2321 """ 2322 Updates kernel model 'model' with extra parameters from 2323 the polydisp and magnetism tab, if the tabs are enabled 2324 """ 2325 if model is None: return 2326 if not hasattr(model, 'setParam'): return 2327 2328 # add polydisperse parameters if asked 2329 if self.chkPolydispersity.isChecked(): 2330 for key, value in self.poly_params.items(): 2331 model.setParam(key, value) 2332 # add magnetic params if asked 2333 if self.chkMagnetism.isChecked(): 2334 for key, value in self.magnet_params.items(): 2335 model.setParam(key, value) 2336 2283 2337 def calculateQGridForModelExt(self, data=None, model=None, completefn=None, use_threads=True): 2284 2338 """ … … 2288 2342 data = self.data 2289 2343 if model is None: 2290 model = self.kernel_module 2344 model = copy.deepcopy(self.kernel_module) 2345 self.updateKernelModelWithExtraParams(model) 2346 2291 2347 if completefn is None: 2292 2348 completefn = self.methodCompleteForData() 2293 2349 smearer = self.smearing_widget.smearer() 2350 weight = FittingUtilities.getWeight(data=data, is2d=self.is2D, flag=self.weighting) 2351 2294 2352 # Awful API to a backend method. 2295 2353 calc_thread = self.methodCalculateForData()(data=data, … … 2300 2358 smearer=smearer, 2301 2359 state=None, 2302 weight= None,2360 weight=weight, 2303 2361 fid=None, 2304 2362 toggle_mode_on=False, … … 2348 2406 residuals = self.calculateResiduals(fitted_data) 2349 2407 self.model_data = fitted_data 2350 2351 new_plots = [fitted_data, residuals] 2408 new_plots = [fitted_data] 2409 if residuals is not None: 2410 new_plots.append(residuals) 2411 2412 if self.data_is_loaded: 2413 GuiUtils.deleteRedundantPlots(self.all_data[self.data_index], new_plots) 2414 else: 2415 # delete theory items for the model, in order to get rid of any redundant items, e.g. beta(Q), S_eff(Q) 2416 self.communicate.deleteIntermediateTheoryPlotsSignal.emit(self.kernel_module.id) 2352 2417 2353 2418 # Create plots for intermediate product data … … 2364 2429 new_plots.append(sq_data) 2365 2430 2366 if self.data_is_loaded:2367 GuiUtils.deleteRedundantPlots(self.all_data[self.data_index], new_plots)2368 2369 # TODO: merge rest of beta approx implementation in2370 # TODO: refactor2371 # deal with constrained radius_effective2372 # for row in range(self._model_model.rowCount()):2373 # if self._model_model.item(row, 0).text() == "radius_effective_mode":2374 # if GuiUtils.toDouble(self._model_model.item(row, 1).text()) == 0:2375 # return2376 # radius_effective = intermediate_ER()2377 # if radius_effective:2378 # for row in range(self._model_model.rowCount()):2379 # if self._model_model.item(row, 0).text() == "radius_effective":2380 # self._model_model.item(row, 1).setText(str(radius_effective))2381 # break2382 2383 2431 for plot in new_plots: 2384 if hasattr(plot, "id") and "esidual" in plot.id: 2385 # TODO: fix updates to residuals plot 2386 pass 2387 elif plot is not None: 2388 self.communicate.plotUpdateSignal.emit([plot]) 2432 self.communicate.plotUpdateSignal.emit([plot]) 2389 2433 2390 2434 def complete2D(self, return_data): … … 2393 2437 """ 2394 2438 fitted_data = self.logic.new2DPlot(return_data) 2395 self.calculateResiduals(fitted_data)2439 residuals = self.calculateResiduals(fitted_data) 2396 2440 self.model_data = fitted_data 2441 new_plots = [fitted_data] 2442 if residuals is not None: 2443 new_plots.append(residuals) 2444 2445 # Update/generate plots 2446 for plot in new_plots: 2447 self.communicate.plotUpdateSignal.emit([plot]) 2397 2448 2398 2449 def calculateResiduals(self, fitted_data): … … 2404 2455 2405 2456 # Modify fitted_data with weighting 2406 self.addWeightingToData(fitted_data)2407 2408 self.createNewIndex( fitted_data)2457 weighted_data = self.addWeightingToData(fitted_data) 2458 2459 self.createNewIndex(weighted_data) 2409 2460 # Calculate difference between return_data and logic.data 2410 self.chi2 = FittingUtilities.calculateChi2( fitted_data, self.logic.data)2461 self.chi2 = FittingUtilities.calculateChi2(weighted_data, self.logic.data) 2411 2462 # Update the control 2412 2463 chi2_repr = "---" if self.chi2 is None else GuiUtils.formatNumber(self.chi2, high=True) 2413 2464 self.lblChi2Value.setText(chi2_repr) 2414 2465 2415 # self.communicate.plotUpdateSignal.emit([fitted_data])2416 2417 2466 # Plot residuals if actual data 2418 2467 if not self.data_is_loaded: 2419 2468 return 2420 2469 2421 residuals_plot = FittingUtilities.plotResiduals(self.data, fitted_data)2470 residuals_plot = FittingUtilities.plotResiduals(self.data, weighted_data) 2422 2471 residuals_plot.id = "Residual " + residuals_plot.id 2423 2472 self.createNewIndex(residuals_plot) … … 2488 2537 self._poly_model.clear() 2489 2538 2539 parameters = self.model_parameters.form_volume_parameters 2540 if self.is2D: 2541 parameters += self.model_parameters.orientation_parameters 2542 2490 2543 [self.setPolyModelParameters(i, param) for i, param in \ 2491 enumerate(self.model_parameters.form_volume_parameters) if param.polydisperse] 2544 enumerate(parameters) if param.polydisperse] 2545 2492 2546 FittingUtilities.addPolyHeadersToModel(self._poly_model) 2493 2547 … … 2522 2576 _, min, max = self.kernel_module.details[param_name] 2523 2577 2578 # Update local param dict 2579 self.poly_params[param_name + '.width'] = width 2580 self.poly_params[param_name + '.npts'] = npts 2581 self.poly_params[param_name + '.nsigmas'] = nsigs 2582 2524 2583 # Construct a row with polydisp. related variable. 2525 2584 # This will get added to the polydisp. model … … 2571 2630 if not self.isCheckable(row): 2572 2631 return 2632 self._model_model.blockSignals(True) 2573 2633 param_name = str(self._model_model.item(row, 0).text()) 2634 self._model_model.blockSignals(False) 2574 2635 if param_name != param.name: 2575 2636 return 2576 2637 # Modify the param value 2638 self._model_model.blockSignals(True) 2577 2639 if self.has_error_column: 2578 2640 # err column changes the indexing … … 2580 2642 else: 2581 2643 self._model_model.item(row, 0).child(0).child(0,4).setText(combo_string) 2644 self._model_model.blockSignals(False) 2582 2645 2583 2646 if combo_string == 'array': … … 2698 2761 param.units] 2699 2762 2763 self.magnet_params[param.name] = param.default 2764 2700 2765 FittingUtilities.addCheckedListToModel(model, checked_list) 2701 2766 … … 2876 2941 # TODO: add polidyspersity and magnetism 2877 2942 2878 2879 2943 def updateUndo(self): 2880 2944 """ … … 2913 2977 if self.all_data: 2914 2978 index = self.all_data[self.data_index] 2979 else: 2980 index = self.theory_item 2915 2981 report_logic = ReportPageLogic(self, 2916 2982 kernel_module=self.kernel_module, … … 3036 3102 # first - regular params 3037 3103 param_list = [] 3104 3105 param_list.append(['model_name', str(self.cbModel.currentText())]) 3038 3106 def gatherParams(row): 3039 3107 """ … … 3122 3190 if lines[0] != 'sasview_parameter_values': 3123 3191 return False 3124 for line in lines[1:-1]: 3192 3193 model = lines[1].split(',') 3194 3195 if model[0] != 'model_name': 3196 return False 3197 3198 context['model_name'] = [model[1]] 3199 for line in lines[2:-1]: 3125 3200 if len(line) != 0: 3126 3201 item = line.split(',') … … 3148 3223 except IndexError: 3149 3224 pass 3225 3226 if str(self.cbModel.currentText()) != str(context['model_name'][0]): 3227 msg = QtWidgets.QMessageBox() 3228 msg.setIcon(QtWidgets.QMessageBox.Information) 3229 msg.setText("The model in the clipboard is not the same as the currently loaded model. \ 3230 Not all parameters saved may paste correctly.") 3231 msg.setStandardButtons(QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel) 3232 result = msg.exec_() 3233 if result == QtWidgets.QMessageBox.Ok: 3234 pass 3235 else: 3236 return 3150 3237 3151 3238 self.updateFullModel(context) … … 3186 3273 param_repr = GuiUtils.formatNumber(param_dict[param_name][3+ioffset], high=True) 3187 3274 self._model_model.item(row, 3+ioffset).setText(param_repr) 3275 self.setFocus() 3276 3188 3277 3189 3278 # block signals temporarily, so we don't end up … … 3192 3281 self.iterateOverModel(updateFittedValues) 3193 3282 self._model_model.blockSignals(False) 3283 3194 3284 3195 3285 def updateFullPolyModel(self, param_dict): … … 3236 3326 param_repr = GuiUtils.formatNumber(param_dict[param_name][5+ioffset], high=True) 3237 3327 self._poly_model.item(row, 5+ioffset).setText(param_repr) 3328 self.setFocus() 3238 3329 3239 3330 # block signals temporarily, so we don't end up … … 3243 3334 self._poly_model.blockSignals(False) 3244 3335 3245
Note: See TracChangeset
for help on using the changeset viewer.