Changeset 5236449 in sasview for src/sas/qtgui/Perspectives/Fitting/FittingWidget.py
- Timestamp:
- Mar 8, 2017 9:35:49 AM (8 years ago)
- 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:
- cbcdd2c
- Parents:
- 86f88d1
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
src/sas/qtgui/Perspectives/Fitting/FittingWidget.py
r86f88d1 r5236449 2 2 import json 3 3 import os 4 import numpy 4 5 from collections import defaultdict 6 7 import logging 8 import traceback 9 5 10 6 11 from PyQt4 import QtGui 7 12 from PyQt4 import QtCore 8 13 9 from UI.FittingWidgetUI import Ui_FittingWidgetUI10 11 14 from sasmodels import generate 12 15 from sasmodels import modelinfo 16 from sasmodels.sasview_model import load_standard_models 17 13 18 from sas.sasgui.guiframe.CategoryInstaller import CategoryInstaller 19 from sas.sasgui.guiframe.dataFitting import Data1D 20 from sas.sasgui.guiframe.dataFitting import Data2D 21 import sas.qtgui.GuiUtils as GuiUtils 22 from sas.sascalc.dataloader.data_info import Detector 23 from sas.sascalc.dataloader.data_info import Source 24 from sas.sasgui.perspectives.fitting.model_thread import Calc1D 25 26 from UI.FittingWidgetUI import Ui_FittingWidgetUI 14 27 15 28 TAB_MAGNETISM = 4 … … 21 34 Main widget for selecting form and structure factor models 22 35 """ 23 def __init__(self, manager=None, parent=None, data=None): 36 signalTheory = QtCore.pyqtSignal(QtGui.QStandardItem) 37 38 def __init__(self, manager=None, parent=None, data=None, id=1): 24 39 """ 25 40 … … 32 47 # Necessary globals 33 48 self.model_is_loaded = False 34 self._data = data 49 self.data_is_loaded = False 50 self.kernel_module = None 35 51 self.is2D = False 36 52 self.model_has_shells = False 37 self.data_assigned = False38 53 self._previous_category_index = 0 39 54 self._last_model_row = 0 55 self._current_parameter_name = None 56 self.models = {} 57 58 # Which tab is this widget displayed in? 59 self.tab_id = id 60 61 # Parameters 62 self.q_range_min = 0.0005 63 self.q_range_max = 0.5 64 self.npts = 20 65 self._data = None 40 66 41 67 # Main GUI setup up 42 68 self.setupUi(self) 43 69 self.setWindowTitle("Fitting") 70 self.communicate = GuiUtils.Communicate() 44 71 45 72 # Set the main models … … 52 79 # Param model displayed in param list 53 80 self.lstParams.setModel(self._model_model) 54 self. _readCategoryInfo()81 self.readCategoryInfo() 55 82 self.model_parameters = None 56 83 self.lstParams.setAlternatingRowColors(True) … … 66 93 self.setTableProperties(self.lstMagnetic) 67 94 68 # Defaults for the str cutre factors95 # Defaults for the structure factors 69 96 self.setDefaultStructureCombo() 70 97 71 # make structure factor and model CBs disabled98 # Make structure factor and model CBs disabled 72 99 self.disableModelCombo() 73 100 self.disableStructureCombo() … … 80 107 self.cbCategory.setCurrentIndex(0) 81 108 109 self._index = data 110 if data is not None: 111 self.data = data 112 82 113 # Connect signals to controls 83 114 self.initializeSignals() … … 93 124 def data(self, value): 94 125 """ data setter """ 95 self._data = value 96 self.data_assigned = True 97 # TODO: update ranges, chi2 etc 126 self._index = value 127 self._data = GuiUtils.dataFromItem(value[0]) 128 self.data_is_loaded = True 129 self.updateQRange() 130 self.cmdFit.setEnabled(True) 98 131 99 132 def acceptsData(self): 100 133 """ Tells the caller this widget can accept new dataset """ 101 return not self.data_ assigned134 return not self.data_is_loaded 102 135 103 136 def disableModelCombo(self): … … 116 149 self.cbStructureFactor.setEnabled(True) 117 150 self.label_4.setEnabled(True) 151 152 def updateQRange(self): 153 """ 154 Updates Q Range display 155 """ 156 if self.data_is_loaded: 157 self.q_range_min, self.q_range_max, self.npts = self.computeDataRange(self.data) 158 # set Q range labels 159 self.lblMinRangeDef.setText(str(self.q_range_min)) 160 self.lblMaxRangeDef.setText(str(self.q_range_max)) 161 162 self.txtMaxRange.setText(str(self.q_range_max)) 163 self.txtMinRange.setText(str(self.q_range_min)) 164 self.txtNpts.setText(str(self.npts)) 118 165 119 166 def initializeControls(self): … … 132 179 self.tabFitting.setTabEnabled(TAB_POLY, False) 133 180 self.tabFitting.setTabEnabled(TAB_MAGNETISM, False) 134 # set initial labels135 self.lblMinRangeDef.setText("---")136 self.lblMaxRangeDef.setText("---")137 181 self.lblChi2Value.setText("---") 182 183 # Update Q Ranges 184 self.updateQRange() 138 185 139 186 def initializeSignals(self): … … 147 194 self.chkPolydispersity.toggled.connect(self.togglePoly) 148 195 self.chkMagnetism.toggled.connect(self.toggleMagnetism) 196 self.cmdFit.clicked.connect(self.onFit) 149 197 150 198 def setDefaultStructureCombo(self): … … 191 239 def selectModel(self): 192 240 """ 193 Select Model from list 194 :return: 241 Respond to select Model from list event 195 242 """ 196 243 model = self.cbModel.currentText() 244 self._current_parameter_name = model 245 246 # SasModel -> QModel 197 247 self.setModelModel(model) 248 249 if self._index is None: 250 if self.is2D: 251 self.createDefault2dData() 252 else: 253 self.createDefault1dData() 254 self.createTheoryIndex() 255 else: 256 # TODO: 2D case 257 # TODO: attach the chart to index 258 self.calculate1DForModel() 198 259 199 260 def selectStructureFactor(self): … … 206 267 self.setModelModel(model) 207 268 208 def _readCategoryInfo(self):269 def readCategoryInfo(self): 209 270 """ 210 271 Reads the categories in from file … … 220 281 cat_file = open(categorization_file, 'rb') 221 282 self.master_category_dict = json.load(cat_file) 222 self. _regenerate_model_dict()283 self.regenerateModelDict() 223 284 cat_file.close() 224 285 except IOError: … … 228 289 print 'An existential crisis if there ever was one.' 229 290 230 def _regenerate_model_dict(self): 291 # Load the model dict 292 models = load_standard_models() 293 for model in models: 294 self.models[model.name] = model 295 296 def regenerateModelDict(self): 231 297 """ 232 298 regenerates self.by_model_dict which has each model name as the … … 313 379 Setting model parameters into table based on selected 314 380 :param model_name: 315 :return:316 381 """ 317 382 # Crete/overwrite model items 318 383 self._model_model.clear() 319 384 model_name = str(model_name) 385 320 386 kernel_module = generate.load_kernel_module(model_name) 387 #model_info = modelinfo.make_model_info(kernel_module) 388 #self.kernel_module = _make_model_from_info(model_info) 321 389 self.model_parameters = modelinfo.make_parameter_table(getattr(kernel_module, 'parameters', [])) 322 390 323 #TODO: scale and background are implicit in sasmodels and needs to be added 391 # Instantiate the current model 392 self.kernel_module = self.models[model_name]() 393 394 # Explicitly add scale and background with default values 324 395 self.addScaleToModel(self._model_model) 325 396 self.addBackgroundToModel(self._model_model) 326 397 398 # Update the QModel 327 399 self.addParametersToModel(self.model_parameters, self._model_model) 328 329 400 self.addHeadersToModel(self._model_model) 330 401 # Multishell models need additional treatment 331 402 self.addExtraShells() 332 403 404 # Add polydispersity to the model 333 405 self.setPolyModel() 406 # Add magnetic parameters to the model 334 407 self.setMagneticModel() 408 409 # Now we claim the model has been loaded 335 410 self.model_is_loaded = True 411 412 # Update Q Ranges 413 self.updateQRange() 414 415 def computeDataRange(self, data): 416 """ 417 compute the minimum and the maximum range of the data 418 return the npts contains in data 419 """ 420 assert(data is not None) 421 assert((isinstance(data, Data1D) or isinstance(data, Data2D))) 422 qmin, qmax, npts = None, None, None 423 if isinstance(data, Data1D): 424 try: 425 qmin = min(data.x) 426 qmax = max(data.x) 427 npts = len(data.x) 428 except: 429 msg = "Unable to find min/max/length of \n data named %s" % \ 430 data.filename 431 raise ValueError, msg 432 433 else: 434 qmin = 0 435 try: 436 x = max(numpy.fabs(data.xmin), numpy.fabs(data.xmax)) 437 y = max(numpy.fabs(data.ymin), numpy.fabs(data.ymax)) 438 except: 439 msg = "Unable to find min/max of \n data named %s" % \ 440 data.filename 441 raise ValueError, msg 442 qmax = math.sqrt(x * x + y * y) 443 npts = len(data.data) 444 return qmin, qmax, npts 336 445 337 446 def addParametersToModel(self, parameters, model): … … 379 488 self._last_model_row = self._model_model.rowCount() 380 489 381 def modelToFittingParameters(self): 490 def createDefault1dData(self): 491 """ 492 Create default data for fitting perspective 493 Only when the page is on theory mode. 494 """ 495 x = numpy.linspace(start=self.q_range_min, stop=self.q_range_max, 496 num=self.npts, endpoint=True) 497 self._data = Data1D(x=x) 498 self._data.xaxis('\\rm{Q}', "A^{-1}") 499 self._data.yaxis('\\rm{Intensity}', "cm^{-1}") 500 self._data.is_data = False 501 self._data.id = str(self.tab_id) + " data" 502 self._data.group_id = str(self.tab_id) + " Model1D" 503 504 def createDefault2dData(self): 505 """ 506 Create 2D data by default 507 Only when the page is on theory mode. 508 """ 509 self._data = Data2D() 510 qmax = self.q_range_max / numpy.sqrt(2) 511 self._data.xaxis('\\rm{Q_{x}}', 'A^{-1}') 512 self._data.yaxis('\\rm{Q_{y}}', 'A^{-1}') 513 self._data.is_data = False 514 self._data.id = str(self.tab_id) + " data" 515 self._data.group_id = str(self.tab_id) + " Model2D" 516 517 # Default detector 518 self._data.detector.append(Detector()) 519 index = len(self._data.detector) - 1 520 self._data.detector[index].distance = 8000 # mm 521 self._data.source.wavelength = 6 # A 522 self._data.detector[index].pixel_size.x = 5 # mm 523 self._data.detector[index].pixel_size.y = 5 # mm 524 self._data.detector[index].beam_center.x = qmax 525 self._data.detector[index].beam_center.y = qmax 526 # theory default: assume the beam 527 #center is located at the center of sqr detector 528 xmax = qmax 529 xmin = -qmax 530 ymax = qmax 531 ymin = -qmax 532 qstep = self.npts 533 534 x = numpy.linspace(start=xmin, stop=xmax, num=qstep, endpoint=True) 535 y = numpy.linspace(start=ymin, stop=ymax, num=qstep, endpoint=True) 536 # Use data info instead 537 new_x = numpy.tile(x, (len(y), 1)) 538 new_y = numpy.tile(y, (len(x), 1)) 539 new_y = new_y.swapaxes(0, 1) 540 541 # all data required in 1d array 542 qx_data = new_x.flatten() 543 qy_data = new_y.flatten() 544 q_data = numpy.sqrt(qx_data * qx_data + qy_data * qy_data) 545 546 # set all True (standing for unmasked) as default 547 mask = numpy.ones(len(qx_data), dtype=bool) 548 # calculate the range of qx and qy: this way, 549 # it is a little more independent 550 # store x and y bin centers in q space 551 x_bins = x 552 y_bins = y 553 554 self._data.source = Source() 555 self._data.data = numpy.ones(len(mask)) 556 self._data.err_data = numpy.ones(len(mask)) 557 self._data.qx_data = qx_data 558 self._data.qy_data = qy_data 559 self._data.q_data = q_data 560 self._data.mask = mask 561 self._data.x_bins = x_bins 562 self._data.y_bins = y_bins 563 # max and min taking account of the bin sizes 564 self._data.xmin = xmin 565 self._data.xmax = xmax 566 self._data.ymin = ymin 567 self._data.ymax = ymax 568 569 def createTheoryIndex(self): 570 """ 571 Create a QStandardModelIndex containing default model data 572 """ 573 name = self._current_parameter_name 574 if self.is2D: 575 name += "2d" 576 name = "M%i [%s]" % (self.tab_id, name) 577 new_item = GuiUtils.createModelItemWithPlot(QtCore.QVariant(self.data), name=name) 578 self.signalTheory.emit(new_item) 579 580 def onFit(self): 581 """ 582 Perform fitting on the current data 583 """ 584 # TEST FOR DISPLAY. 585 self.calculate1DForModel() 586 587 def calculate1DForModel(self): 382 588 """ 383 589 Prepare the fitting data object, based on current ModelModel 384 590 """ 385 pass 591 data = self.data 592 model = self.kernel_module 593 page_id = 0 594 qmin = self.q_range_min 595 qmax = self.q_range_max 596 smearer = None 597 state = None 598 weight = None 599 fid = None 600 toggle_mode_on = False 601 update_chisqr = False 602 source = None 603 604 self.calc_1D = Calc1D(data=data, 605 model=model, 606 page_id=page_id, 607 qmin=qmin, 608 qmax=qmax, 609 smearer=smearer, 610 state=state, 611 weight=weight, 612 fid=fid, 613 toggle_mode_on=toggle_mode_on, 614 completefn=self.complete1D, 615 update_chisqr=update_chisqr, 616 exception_handler=self.calcException, 617 source=source) 618 self.calc_1D.queue() 619 620 def complete1D(self, x, y, page_id, elapsed, index, model, 621 weight=None, fid=None, 622 toggle_mode_on=False, state=None, 623 data=None, update_chisqr=True, 624 source='model', plot_result=True): 625 """ 626 Plot the current data 627 Should be a rewrite of fitting.py/_complete1D 628 """ 629 print "THREAD FINISHED" 630 631 def calcException(self, etype, value, tb): 632 """ 633 """ 634 print "THREAD EXCEPTION" 635 logging.error("".join(traceback.format_exception(etype, value, tb))) 636 msg = traceback.format_exception(etype, value, tb, limit=1) 386 637 387 638 def replaceShellName(self, param_name, value):
Note: See TracChangeset
for help on using the changeset viewer.