Ignore:
Timestamp:
Mar 8, 2017 9:35:49 AM (8 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:
cbcdd2c
Parents:
86f88d1
Message:

Default datasets for fitting SASVIEW-498

File:
1 edited

Legend:

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

    r86f88d1 r5236449  
    22import json 
    33import  os 
     4import numpy 
    45from collections import defaultdict 
     6 
     7import logging 
     8import traceback 
     9 
    510 
    611from PyQt4 import QtGui 
    712from PyQt4 import QtCore 
    813 
    9 from UI.FittingWidgetUI import Ui_FittingWidgetUI 
    10  
    1114from sasmodels import generate 
    1215from sasmodels import modelinfo 
     16from sasmodels.sasview_model import load_standard_models 
     17 
    1318from sas.sasgui.guiframe.CategoryInstaller import CategoryInstaller 
     19from sas.sasgui.guiframe.dataFitting import Data1D 
     20from sas.sasgui.guiframe.dataFitting import Data2D 
     21import sas.qtgui.GuiUtils as GuiUtils 
     22from sas.sascalc.dataloader.data_info import Detector 
     23from sas.sascalc.dataloader.data_info import Source 
     24from sas.sasgui.perspectives.fitting.model_thread import Calc1D 
     25 
     26from UI.FittingWidgetUI import Ui_FittingWidgetUI 
    1427 
    1528TAB_MAGNETISM = 4 
     
    2134    Main widget for selecting form and structure factor models 
    2235    """ 
    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): 
    2439        """ 
    2540 
     
    3247        # Necessary globals 
    3348        self.model_is_loaded = False 
    34         self._data = data 
     49        self.data_is_loaded = False 
     50        self.kernel_module = None 
    3551        self.is2D = False 
    3652        self.model_has_shells = False 
    37         self.data_assigned = False 
    3853        self._previous_category_index = 0 
    3954        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 
    4066 
    4167        # Main GUI setup up 
    4268        self.setupUi(self) 
    4369        self.setWindowTitle("Fitting") 
     70        self.communicate = GuiUtils.Communicate() 
    4471 
    4572        # Set the main models 
     
    5279        # Param model displayed in param list 
    5380        self.lstParams.setModel(self._model_model) 
    54         self._readCategoryInfo() 
     81        self.readCategoryInfo() 
    5582        self.model_parameters = None 
    5683        self.lstParams.setAlternatingRowColors(True) 
     
    6693        self.setTableProperties(self.lstMagnetic) 
    6794 
    68         # Defaults for the strcutre factors 
     95        # Defaults for the structure factors 
    6996        self.setDefaultStructureCombo() 
    7097 
    71         # make structure factor and model CBs disabled 
     98        # Make structure factor and model CBs disabled 
    7299        self.disableModelCombo() 
    73100        self.disableStructureCombo() 
     
    80107        self.cbCategory.setCurrentIndex(0) 
    81108 
     109        self._index = data 
     110        if data is not None: 
     111            self.data = data 
     112 
    82113        # Connect signals to controls 
    83114        self.initializeSignals() 
     
    93124    def data(self, value): 
    94125        """ 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) 
    98131 
    99132    def acceptsData(self): 
    100133        """ Tells the caller this widget can accept new dataset """ 
    101         return not self.data_assigned 
     134        return not self.data_is_loaded 
    102135 
    103136    def disableModelCombo(self): 
     
    116149        self.cbStructureFactor.setEnabled(True) 
    117150        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)) 
    118165 
    119166    def initializeControls(self): 
     
    132179        self.tabFitting.setTabEnabled(TAB_POLY, False) 
    133180        self.tabFitting.setTabEnabled(TAB_MAGNETISM, False) 
    134         # set initial labels 
    135         self.lblMinRangeDef.setText("---") 
    136         self.lblMaxRangeDef.setText("---") 
    137181        self.lblChi2Value.setText("---") 
     182     
     183        # Update Q Ranges 
     184        self.updateQRange() 
    138185 
    139186    def initializeSignals(self): 
     
    147194        self.chkPolydispersity.toggled.connect(self.togglePoly) 
    148195        self.chkMagnetism.toggled.connect(self.toggleMagnetism) 
     196        self.cmdFit.clicked.connect(self.onFit) 
    149197 
    150198    def setDefaultStructureCombo(self): 
     
    191239    def selectModel(self): 
    192240        """ 
    193         Select Model from list 
    194         :return: 
     241        Respond to select Model from list event 
    195242        """ 
    196243        model = self.cbModel.currentText() 
     244        self._current_parameter_name = model 
     245 
     246        # SasModel -> QModel 
    197247        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() 
    198259 
    199260    def selectStructureFactor(self): 
     
    206267        self.setModelModel(model) 
    207268 
    208     def _readCategoryInfo(self): 
     269    def readCategoryInfo(self): 
    209270        """ 
    210271        Reads the categories in from file 
     
    220281            cat_file = open(categorization_file, 'rb') 
    221282            self.master_category_dict = json.load(cat_file) 
    222             self._regenerate_model_dict() 
     283            self.regenerateModelDict() 
    223284            cat_file.close() 
    224285        except IOError: 
     
    228289            print 'An existential crisis if there ever was one.' 
    229290 
    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): 
    231297        """ 
    232298        regenerates self.by_model_dict which has each model name as the 
     
    313379        Setting model parameters into table based on selected 
    314380        :param model_name: 
    315         :return: 
    316381        """ 
    317382        # Crete/overwrite model items 
    318383        self._model_model.clear() 
    319384        model_name = str(model_name) 
     385 
    320386        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) 
    321389        self.model_parameters = modelinfo.make_parameter_table(getattr(kernel_module, 'parameters', [])) 
    322390 
    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 
    324395        self.addScaleToModel(self._model_model) 
    325396        self.addBackgroundToModel(self._model_model) 
    326397 
     398        # Update the QModel 
    327399        self.addParametersToModel(self.model_parameters, self._model_model) 
    328  
    329400        self.addHeadersToModel(self._model_model) 
    330  
     401        # Multishell models need additional treatment 
    331402        self.addExtraShells() 
    332403 
     404        # Add polydispersity to the model 
    333405        self.setPolyModel() 
     406        # Add magnetic parameters to the model 
    334407        self.setMagneticModel() 
     408 
     409        # Now we claim the model has been loaded 
    335410        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 
    336445 
    337446    def addParametersToModel(self, parameters, model): 
     
    379488        self._last_model_row = self._model_model.rowCount() 
    380489 
    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): 
    382588        """ 
    383589        Prepare the fitting data object, based on current ModelModel 
    384590        """ 
    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) 
    386637 
    387638    def replaceShellName(self, param_name, value): 
Note: See TracChangeset for help on using the changeset viewer.