source: sasview/src/sas/qtgui/Perspectives/Fitting/FittingUtilities.py @ e0d5b63

ESS_GUI_opencl
Last change on this file since e0d5b63 was c7e73e7, checked in by Piotr Rozyczko <piotr.rozyczko@…>, 6 years ago

Disable checkboxes by unfittable parameter rows. Trac #1250

  • Property mode set to 100644
File size: 29.1 KB
RevLine 
[fde5bcd]1import copy
[1bc27f1]2
[4992ff2]3from PyQt5 import QtCore
4from PyQt5 import QtGui
[4d457df]5
[6fd4e36]6import numpy
7
[dc5ef15]8from sas.qtgui.Plotting.PlotterData import Data1D
9from sas.qtgui.Plotting.PlotterData import Data2D
[6fd4e36]10
[04f775d]11from sas.qtgui.Perspectives.Fitting.AssociatedComboBox import AssociatedComboBox
12
[f54ce30]13model_header_captions = ['Parameter', 'Value', 'Min', 'Max', 'Units']
14
15model_header_tooltips = ['Select parameter for fitting',
[d0dfcb2]16                         'Enter parameter value',
17                         'Enter minimum value for parameter',
18                         'Enter maximum value for parameter',
19                         'Unit of the parameter']
[f54ce30]20
21poly_header_captions = ['Parameter', 'PD[ratio]', 'Min', 'Max', 'Npts', 'Nsigs',
[d0dfcb2]22                        'Function', 'Filename']
[f54ce30]23
24poly_header_tooltips = ['Select parameter for fitting',
[1a15ada]25                        'Enter polydispersity ratio (Std deviation/mean).\n'+
[8faac15]26                        'For angles this can be either std deviation or half width (for uniform distributions) in degrees',
[d0dfcb2]27                        'Enter minimum value for parameter',
28                        'Enter maximum value for parameter',
29                        'Enter number of points for parameter',
30                        'Enter number of sigmas parameter',
31                        'Select distribution function',
32                        'Select filename with user-definable distribution']
[f54ce30]33
34error_tooltip = 'Error value for fitted parameter'
35header_error_caption = 'Error'
36
[4d457df]37def replaceShellName(param_name, value):
38    """
39    Updates parameter name from <param_name>[n_shell] to <param_name>value
40    """
41    assert '[' in param_name
42    return param_name[:param_name.index('[')]+str(value)
43
44def getIterParams(model):
45    """
46    Returns a list of all multi-shell parameters in 'model'
47    """
[b3e8629]48    return list([par for par in model.iq_parameters if "[" in par.name])
[4d457df]49
50def getMultiplicity(model):
51    """
52    Finds out if 'model' has multishell parameters.
53    If so, returns the name of the counter parameter and the number of shells
54    """
55    iter_params = getIterParams(model)
[a9b568c]56    param_name = ""
57    param_length = 0
58    if iter_params:
59        param_length = iter_params[0].length
60        param_name = iter_params[0].length_control
61        if param_name is None and '[' in iter_params[0].name:
62            param_name = iter_params[0].name[:iter_params[0].name.index('[')]
63    return (param_name, param_length)
[4d457df]64
[04f775d]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.
[4d457df]71    """
[04f775d]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
[04ce9ac]77    if isinstance(param.choices, (list, tuple)) and len(param.choices) > 0:
[04f775d]78        # The choices property is concrete in sasmodels, probably will use this
79        choices = param.choices
[04ce9ac]80    elif isinstance(param.units, (list, tuple)):
81        choices = [str(x) for x in param.units]
[04f775d]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
[a758043]93def addParametersToModel(parameters, kernel_module, is2D, model=None, view=None):
[4d457df]94    """
[a758043]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.
[4d457df]98    """
99    multishell_parameters = getIterParams(parameters)
100    multishell_param_name, _ = getMultiplicity(parameters)
[57be490]101
[b5cc06e]102    if is2D:
103        params = [p for p in parameters.kernel_parameters if p.type != 'magnetic']
104    else:
[8b745c36]105        params = parameters.iq_parameters
[04f775d]106
[a758043]107    rows = []
[1970780]108    for param in params:
[4d457df]109        # don't include shell parameters
110        if param.name == multishell_param_name:
111            continue
[04f775d]112
[4d457df]113        # Modify parameter name from <param>[n] to <param>1
114        item_name = param.name
115        if param in multishell_parameters:
[b1e36a3]116            continue
[4d457df]117
118        item1 = QtGui.QStandardItem(item_name)
119        item1.setCheckable(True)
[2add354]120        item1.setEditable(False)
[04f775d]121
[4d457df]122        # check for polydisp params
123        if param.polydisperse:
124            poly_item = QtGui.QStandardItem("Polydispersity")
[2add354]125            poly_item.setEditable(False)
[4d457df]126            item1_1 = QtGui.QStandardItem("Distribution")
[2add354]127            item1_1.setEditable(False)
[04f775d]128
[4d457df]129            # Find param in volume_params
[6889ba2]130            poly_pars = copy.deepcopy(parameters.form_volume_parameters)
[f3cc979]131            if is2D:
132                poly_pars += parameters.orientation_parameters
133            for p in poly_pars:
[4d457df]134                if p.name != param.name:
135                    continue
[aca8418]136                width = kernel_module.getParam(p.name+'.width')
[8e2cd79]137                ptype = kernel_module.getParam(p.name+'.type')
[aca8418]138                item1_2 = QtGui.QStandardItem(str(width))
[2add354]139                item1_2.setEditable(False)
[aca8418]140                item1_3 = QtGui.QStandardItem()
[2add354]141                item1_3.setEditable(False)
[aca8418]142                item1_4 = QtGui.QStandardItem()
[2add354]143                item1_4.setEditable(False)
[8e2cd79]144                item1_5 = QtGui.QStandardItem(ptype)
[2add354]145                item1_5.setEditable(False)
[4d457df]146                poly_item.appendRow([item1_1, item1_2, item1_3, item1_4, item1_5])
147                break
[04f775d]148
[4d457df]149            # Add the polydisp item as a child
150            item1.appendRow([poly_item])
[04f775d]151
[4d457df]152        # Param values
153        item2 = QtGui.QStandardItem(str(param.default))
154        item3 = QtGui.QStandardItem(str(param.limits[0]))
155        item4 = QtGui.QStandardItem(str(param.limits[1]))
[04f775d]156        item5 = QtGui.QStandardItem(str(param.units))
[2add354]157        item5.setEditable(False)
[4d457df]158
[04f775d]159        # Check if fixed-choice (returns combobox, if so, also makes some items uneditable)
160        row = [item1, item2, item3, item4, item5]
161        cbox = createFixedChoiceComboBox(param, row)
162
163        # Append to the model and use the combobox, if required
[a758043]164        if None not in (model, view):
165            model.appendRow(row)
166            if cbox:
167                view.setIndexWidget(item2.index(), cbox)
[c7e73e7]168
[a758043]169        rows.append(row)
170
171    return rows
[04f775d]172
[01b4877]173def addSimpleParametersToModel(parameters, is2D, parameters_original=None, model=None, view=None, row_num=None):
[4d457df]174    """
[04f775d]175    Update local ModelModel with sasmodel parameters (non-dispersed, non-magnetic)
[bc7371fd]176    Actually appends to model, if model and view params are not None.
177    Always returns list of lists of QStandardItems.
[b69b549]178
179    parameters_original: list of parameters before any tagging on their IDs, e.g. for product model (so that those are
180    the display names; see below)
[4d457df]181    """
[b5cc06e]182    if is2D:
183        params = [p for p in parameters.kernel_parameters if p.type != 'magnetic']
184    else:
185        params = parameters.iq_parameters
[04f775d]186
[700b03b]187    if parameters_original:
188        # 'parameters_original' contains the parameters as they are to be DISPLAYED, while 'parameters'
189        # contains the parameters as they were renamed; this is for handling name collisions in product model.
190        # The 'real name' of the parameter will be stored in the item's user data.
191        if is2D:
192            params_orig = [p for p in parameters_original.kernel_parameters if p.type != 'magnetic']
193        else:
194            params_orig = parameters_original.iq_parameters
195    else:
196        # no difference in names anyway
197        params_orig = params
198
[a758043]199    rows = []
[700b03b]200    for param, param_orig in zip(params, params_orig):
[7248d75d]201        # Create the top level, checkable item
[700b03b]202        item_name = param_orig.name
[4d457df]203        item1 = QtGui.QStandardItem(item_name)
[700b03b]204        item1.setData(param.name, QtCore.Qt.UserRole)
[c7e73e7]205        item1.setCheckable(False)
[2add354]206        item1.setEditable(False)
[04f775d]207
[4d457df]208        # Param values
[2add354]209        # TODO: add delegate for validation of cells
[4d457df]210        item2 = QtGui.QStandardItem(str(param.default))
[04f775d]211        item3 = QtGui.QStandardItem(str(param.limits[0]))
212        item4 = QtGui.QStandardItem(str(param.limits[1]))
213        item5 = QtGui.QStandardItem(str(param.units))
214        item5.setEditable(False)
215
216        # Check if fixed-choice (returns combobox, if so, also makes some items uneditable)
217        row = [item1, item2, item3, item4, item5]
218        cbox = createFixedChoiceComboBox(param, row)
219
220        # Append to the model and use the combobox, if required
[a758043]221        if None not in (model, view):
[c7e73e7]222
[01b4877]223            if row_num is None:
224                model.appendRow(row)
225            else:
226                model.insertRow(row_num, row)
227                row_num += 1
[a758043]228            if cbox:
[c7e73e7]229                item1.setCheckable(False)
230                item3.setText("")
231                item4.setText("")
232                item3.setEditable(False)
233                item4.setEditable(False)
[a758043]234                view.setIndexWidget(item2.index(), cbox)
[c7e73e7]235            else:
236                item1.setCheckable(True)
[01b4877]237
[a758043]238        rows.append(row)
239
240    return rows
[4d457df]241
[18d5c94a]242def markParameterDisabled(model, row):
[b87dc1a]243    """Given the QModel row number, format to show it is not available for fitting"""
244
245    # If an error column is present, there are a total of 6 columns.
246    items = [model.item(row, c) for c in range(6)]
[18d5c94a]247
248    model.blockSignals(True)
249
250    for item in items:
251        if item is None:
252            continue
253        item.setEditable(False)
254        item.setCheckable(False)
255
256    item = items[0]
257
258    font = QtGui.QFont()
259    font.setItalic(True)
260    item.setFont(font)
261    item.setForeground(QtGui.QBrush(QtGui.QColor(100, 100, 100)))
262    item.setToolTip("This parameter cannot be fitted.")
263
264    model.blockSignals(False)
265
[4d457df]266def addCheckedListToModel(model, param_list):
267    """
268    Add a QItem to model. Makes the QItem checkable
269    """
270    assert isinstance(model, QtGui.QStandardItemModel)
271    item_list = [QtGui.QStandardItem(item) for item in param_list]
272    item_list[0].setCheckable(True)
273    model.appendRow(item_list)
274
[00b7ddf0]275def addHeadingRowToModel(model, name):
276    """adds a non-interactive top-level row to the model"""
277    header_row = [QtGui.QStandardItem() for i in range(5)]
278    header_row[0].setText(name)
279
280    font = header_row[0].font()
281    font.setBold(True)
282    header_row[0].setFont(font)
283
284    for item in header_row:
285        item.setEditable(False)
286        item.setCheckable(False)
287        item.setSelectable(False)
288
289    model.appendRow(header_row)
290
[4d457df]291def addHeadersToModel(model):
292    """
293    Adds predefined headers to the model
294    """
[f54ce30]295    for i, item in enumerate(model_header_captions):
[b3e8629]296        model.setHeaderData(i, QtCore.Qt.Horizontal, item)
[f54ce30]297
[fde5bcd]298    model.header_tooltips = copy.copy(model_header_tooltips)
[4d457df]299
[f182f93]300def addErrorHeadersToModel(model):
301    """
302    Adds predefined headers to the model
303    """
[fde5bcd]304    model_header_error_captions = copy.copy(model_header_captions)
[f54ce30]305    model_header_error_captions.insert(2, header_error_caption)
306    for i, item in enumerate(model_header_error_captions):
[b3e8629]307        model.setHeaderData(i, QtCore.Qt.Horizontal, item)
[f182f93]308
[fde5bcd]309    model_header_error_tooltips = copy.copy(model_header_tooltips)
[f54ce30]310    model_header_error_tooltips.insert(2, error_tooltip)
[fde5bcd]311    model.header_tooltips = copy.copy(model_header_error_tooltips)
[a95c44b]312
[4d457df]313def addPolyHeadersToModel(model):
314    """
315    Adds predefined headers to the model
316    """
[f54ce30]317    for i, item in enumerate(poly_header_captions):
[b3e8629]318        model.setHeaderData(i, QtCore.Qt.Horizontal, item)
[f54ce30]319
[fde5bcd]320    model.header_tooltips = copy.copy(poly_header_tooltips)
[4d457df]321
[a95c44b]322
[aca8418]323def addErrorPolyHeadersToModel(model):
324    """
325    Adds predefined headers to the model
326    """
[fde5bcd]327    poly_header_error_captions = copy.copy(poly_header_captions)
[f54ce30]328    poly_header_error_captions.insert(2, header_error_caption)
329    for i, item in enumerate(poly_header_error_captions):
[b3e8629]330        model.setHeaderData(i, QtCore.Qt.Horizontal, item)
[f54ce30]331
[fde5bcd]332    poly_header_error_tooltips = copy.copy(poly_header_tooltips)
[f54ce30]333    poly_header_error_tooltips.insert(2, error_tooltip)
[fde5bcd]334    model.header_tooltips = copy.copy(poly_header_error_tooltips)
[a95c44b]335
[b69b549]336def addShellsToModel(parameters, model, index, row_num=None, view=None):
[4d457df]337    """
[70f4458]338    Find out multishell parameters and update the model with the requested number of them.
339    Inserts them after the row at row_num, if not None; otherwise, appends to end.
[b69b549]340    If view param is not None, supports fixed-choice params.
[70f4458]341    Returns a list of lists of QStandardItem objects.
[4d457df]342    """
343    multishell_parameters = getIterParams(parameters)
344
[a758043]345    rows = []
[b3e8629]346    for i in range(index):
[4d457df]347        for par in multishell_parameters:
[b1e36a3]348            # Create the name: <param>[<i>], e.g. "sld1" for parameter "sld[n]"
349            param_name = replaceShellName(par.name, i+1)
[4d457df]350            item1 = QtGui.QStandardItem(param_name)
351            item1.setCheckable(True)
352            # check for polydisp params
353            if par.polydisperse:
354                poly_item = QtGui.QStandardItem("Polydispersity")
355                item1_1 = QtGui.QStandardItem("Distribution")
356                # Find param in volume_params
357                for p in parameters.form_volume_parameters:
358                    if p.name != par.name:
359                        continue
360                    item1_2 = QtGui.QStandardItem(str(p.default))
361                    item1_3 = QtGui.QStandardItem(str(p.limits[0]))
362                    item1_4 = QtGui.QStandardItem(str(p.limits[1]))
[88ada06]363                    item1_5 = QtGui.QStandardItem(str(p.units))
[4d457df]364                    poly_item.appendRow([item1_1, item1_2, item1_3, item1_4, item1_5])
365                    break
366                item1.appendRow([poly_item])
367
368            item2 = QtGui.QStandardItem(str(par.default))
369            item3 = QtGui.QStandardItem(str(par.limits[0]))
370            item4 = QtGui.QStandardItem(str(par.limits[1]))
[88ada06]371            item5 = QtGui.QStandardItem(str(par.units))
372            item5.setEditable(False)
373
374            # Check if fixed-choice (returns combobox, if so, also makes some items uneditable)
375            row = [item1, item2, item3, item4, item5]
376            cbox = createFixedChoiceComboBox(par, row)
377
[7f41584]378            # Apply combobox if required
379            if None not in (view, cbox):
380                # set the min/max cell to be empty
381                item3.setText("")
382                item4.setText("")
383
[b69b549]384            # Always add to the model
[70f4458]385            if row_num is None:
386                model.appendRow(row)
387            else:
388                model.insertRow(row_num, row)
389                row_num += 1
[a758043]390
[7f41584]391            if cbox is not None:
[88ada06]392                view.setIndexWidget(item2.index(), cbox)
[4d457df]393
[a758043]394            rows.append(row)
395
396    return rows
[4d457df]397
[6fd4e36]398def calculateChi2(reference_data, current_data):
399    """
400    Calculate Chi2 value between two sets of data
401    """
[ff3b293]402    if reference_data is None or current_data is None:
403        return None
[6fd4e36]404    # WEIGHING INPUT
405    #from sas.sasgui.perspectives.fitting.utils import get_weight
406    #flag = self.get_weight_flag()
407    #weight = get_weight(data=self.data, is2d=self._is_2D(), flag=flag)
[1bc27f1]408    chisqr = None
409    if reference_data is None:
410        return chisqr
[6fd4e36]411
412    # temporary default values for index and weight
413    index = None
414    weight = None
415
416    # Get data: data I, theory I, and data dI in order
417    if isinstance(reference_data, Data2D):
[1bc27f1]418        if index is None:
[6fd4e36]419            index = numpy.ones(len(current_data.data), dtype=bool)
[1bc27f1]420        if weight is not None:
[6fd4e36]421            current_data.err_data = weight
422        # get rid of zero error points
423        index = index & (current_data.err_data != 0)
424        index = index & (numpy.isfinite(current_data.data))
425        fn = current_data.data[index]
426        gn = reference_data.data[index]
427        en = current_data.err_data[index]
428    else:
429        # 1 d theory from model_thread is only in the range of index
[1bc27f1]430        if index is None:
[6fd4e36]431            index = numpy.ones(len(current_data.y), dtype=bool)
[1bc27f1]432        if weight is not None:
[6fd4e36]433            current_data.dy = weight
[1bc27f1]434        if current_data.dy is None or current_data.dy == []:
[6fd4e36]435            dy = numpy.ones(len(current_data.y))
436        else:
437            ## Set consistently w/AbstractFitengine:
438            # But this should be corrected later.
[fde5bcd]439            dy = copy.deepcopy(current_data.dy)
[6fd4e36]440            dy[dy == 0] = 1
441        fn = current_data.y[index]
442        gn = reference_data.y
443        en = dy[index]
444    # Calculate the residual
445    try:
446        res = (fn - gn) / en
447    except ValueError:
[180bd54]448        #print "Chi2 calculations: Unmatched lengths %s, %s, %s" % (len(fn), len(gn), len(en))
[0268aed]449        return None
[6fd4e36]450
451    residuals = res[numpy.isfinite(res)]
452    chisqr = numpy.average(residuals * residuals)
453
454    return chisqr
455
[0268aed]456def residualsData1D(reference_data, current_data):
457    """
[7d077d1]458    Calculate the residuals for difference of two Data1D sets
[0268aed]459    """
460    # temporary default values for index and weight
461    index = None
462    weight = None
463
464    # 1d theory from model_thread is only in the range of index
[180bd54]465    if current_data.dy is None or current_data.dy == []:
[0268aed]466        dy = numpy.ones(len(current_data.y))
467    else:
[180bd54]468        dy = weight if weight is not None else numpy.ones(len(current_data.y))
[0268aed]469        dy[dy == 0] = 1
470    fn = current_data.y[index][0]
471    gn = reference_data.y
472    en = dy[index][0]
[f7d39c9]473
474    # x values
475    x_current = current_data.x
476    x_reference = reference_data.x
477
[0268aed]478    # build residuals
479    residuals = Data1D()
[180bd54]480    if len(fn) == len(gn):
[0268aed]481        y = (fn - gn)/en
482        residuals.y = -y
[f7d39c9]483    elif len(fn) > len(gn):
484        residuals.y = (fn - gn[1:len(fn)])/en
[180bd54]485    else:
[689222c]486        try:
[f7d39c9]487            y = numpy.zeros(len(current_data.y))
488            begin = 0
489            for i, x_value in enumerate(x_reference):
490                if x_value in x_current:
491                    begin = i
492                    break
493            end = len(x_reference)
494            endl = 0
495            for i, x_value in enumerate(list(x_reference)[::-1]):
496                if x_value in x_current:
497                    endl = i
498                    break
499
500            y = (fn - gn[begin:end-endl])/en
[689222c]501            residuals.y = y
502        except ValueError:
503            # value errors may show up every once in a while for malformed columns,
504            # just reuse what's there already
505            pass
[180bd54]506
[0268aed]507    residuals.x = current_data.x[index][0]
[f6c19cf]508    residuals.dy = numpy.ones(len(residuals.y))
[0268aed]509    residuals.dx = None
510    residuals.dxl = None
511    residuals.dxw = None
512    residuals.ytransform = 'y'
[1bc27f1]513    # For latter scale changes
[0268aed]514    residuals.xaxis('\\rm{Q} ', 'A^{-1}')
515    residuals.yaxis('\\rm{Residuals} ', 'normalized')
516
517    return residuals
518
519def residualsData2D(reference_data, current_data):
520    """
[7d077d1]521    Calculate the residuals for difference of two Data2D sets
[0268aed]522    """
523    # temporary default values for index and weight
[1bc27f1]524    # index = None
[0268aed]525    weight = None
526
527    # build residuals
528    residuals = Data2D()
529    # Not for trunk the line below, instead use the line above
530    current_data.clone_without_data(len(current_data.data), residuals)
531    residuals.data = None
532    fn = current_data.data
533    gn = reference_data.data
[180bd54]534    en = current_data.err_data if weight is None else weight
[0268aed]535    residuals.data = (fn - gn) / en
536    residuals.qx_data = current_data.qx_data
537    residuals.qy_data = current_data.qy_data
538    residuals.q_data = current_data.q_data
539    residuals.err_data = numpy.ones(len(residuals.data))
540    residuals.xmin = min(residuals.qx_data)
541    residuals.xmax = max(residuals.qx_data)
542    residuals.ymin = min(residuals.qy_data)
543    residuals.ymax = max(residuals.qy_data)
544    residuals.q_data = current_data.q_data
545    residuals.mask = current_data.mask
546    residuals.scale = 'linear'
547    # check the lengths
548    if len(residuals.data) != len(residuals.q_data):
549        return None
550    return residuals
551
552def plotResiduals(reference_data, current_data):
553    """
554    Create Data1D/Data2D with residuals, ready for plotting
555    """
[fde5bcd]556    data_copy = copy.deepcopy(current_data)
[0268aed]557    # Get data: data I, theory I, and data dI in order
558    method_name = current_data.__class__.__name__
559    residuals_dict = {"Data1D": residualsData1D,
560                      "Data2D": residualsData2D}
561
[712db9e]562    try:
563        residuals = residuals_dict[method_name](reference_data, data_copy)
564    except ValueError:
565        return None
[0268aed]566
567    theory_name = str(current_data.name.split()[0])
[6b50296]568    res_name = reference_data.filename if reference_data.filename else reference_data.name
569    residuals.name = "Residuals for " + str(theory_name) + "[" + res_name + "]"
[0268aed]570    residuals.title = residuals.name
[f182f93]571    residuals.ytransform = 'y'
572
[0268aed]573    # when 2 data have the same id override the 1 st plotted
574    # include the last part if keeping charts for separate models is required
575    residuals.id = "res" + str(reference_data.id) # + str(theory_name)
576    # group_id specify on which panel to plot this data
577    group_id = reference_data.group_id
578    residuals.group_id = "res" + str(group_id)
[1bc27f1]579
[0268aed]580    # Symbol
581    residuals.symbol = 0
582    residuals.hide_error = False
583
584    return residuals
585
[44deced]586def plotPolydispersities(model):
[3ae70f9]587    plots = []
588    if model is None:
589        return plots
590    # test for model being a sasmodels.sasview_model.SasviewModel?
[5d3af9f]591    for name in model.dispersion.keys():
592        xarr, yarr = model.get_weights(name)
593        if len(xarr) <= 1: # param name not found or no polydisp.
[aa82f54]594            continue
[3ae70f9]595        # create Data1D as in residualsData1D() and fill x/y members
596        # similar to FittingLogic._create1DPlot() but different data/axes
[16287f3]597        data1d = Data1D(x=xarr, y=yarr)
[0177eb6]598        xunit = model.details[name][0]
599        data1d.xaxis(r'\rm{{{}}}'.format(name.replace('_', '\_')), xunit)
[f5e2a10a]600        data1d.yaxis(r'\rm{probability}', 'normalized')
[3ae70f9]601        data1d.scale = 'linear'
602        data1d.symbol = 'Line'
603        data1d.name = "{} polydispersity".format(name)
604        data1d.id = data1d.name # placeholder, has to be completed later
[5990185]605        data1d.plot_role = Data1D.ROLE_RESIDUAL
[3ae70f9]606        plots.append(data1d)
607    return plots
608
[6fd4e36]609def binary_encode(i, digits):
[b3e8629]610    return [i >> d & 1 for d in range(digits)]
[6fd4e36]611
[fd1ae6d1]612def getWeight(data, is2d, flag=None):
613    """
614    Received flag and compute error on data.
615    :param flag: flag to transform error of data.
616    """
617    weight = None
[b764ae5]618    if data is None:
619        return []
[fd1ae6d1]620    if is2d:
[b764ae5]621        if not hasattr(data, 'err_data'):
622            return []
[fd1ae6d1]623        dy_data = data.err_data
624        data = data.data
625    else:
[b764ae5]626        if not hasattr(data, 'dy'):
627            return []
[fd1ae6d1]628        dy_data = data.dy
629        data = data.y
630
631    if flag == 0:
632        weight = numpy.ones_like(data)
633    elif flag == 1:
634        weight = dy_data
635    elif flag == 2:
636        weight = numpy.sqrt(numpy.abs(data))
637    elif flag == 3:
638        weight = numpy.abs(data)
639    return weight
[d4dac80]640
641def updateKernelWithResults(kernel, results):
642    """
643    Takes model kernel and applies results dict to its parameters,
644    returning the modified (deep) copy of the kernel.
645    """
[8e2cd79]646    assert isinstance(results, dict)
[d4dac80]647    local_kernel = copy.deepcopy(kernel)
648
649    for parameter in results.keys():
650        # Update the parameter value - note: this supports +/-inf as well
651        local_kernel.setParam(parameter, results[parameter][0])
652
653    return local_kernel
654
655
[57be490]656def getStandardParam(model=None):
657    """
658    Returns a list with standard parameters for the current model
659    """
660    param = []
661    num_rows = model.rowCount()
662    if num_rows < 1:
[cf9f39e]663        return param
[57be490]664
665    for row in range(num_rows):
666        param_name = model.item(row, 0).text()
[8e2cd79]667        checkbox_state = model.item(row, 0).checkState() == QtCore.Qt.Checked
668        value = model.item(row, 1).text()
[57be490]669        column_shift = 0
670        if model.columnCount() == 5: # no error column
671            error_state = False
672            error_value = 0.0
673        else:
674            error_state = True
675            error_value = model.item(row, 2).text()
676            column_shift = 1
677        min_state = True
678        max_state = True
679        min_value = model.item(row, 2+column_shift).text()
680        max_value = model.item(row, 3+column_shift).text()
681        unit = ""
682        if model.item(row, 4+column_shift) is not None:
[9817207]683            u = model.item(row, 4+column_shift).text()
684            # This isn't a unit if it is a number (polyd./magn.)
685            unit = "" if isNumber(u) else u
[57be490]686        param.append([checkbox_state, param_name, value, "",
[9817207]687                     [error_state, error_value],
688                     [min_state, min_value],
689                     [max_state, max_value], unit])
[57be490]690
691    return param
692
[9817207]693def isNumber(s):
694    """
695    Checks if string 's' is an int/float
696    """
697    if s.isdigit():
698        # check int
699        return True
700    else:
701        try:
702            # check float
703            _ = float(s)
704        except ValueError:
705            return False
706    return True
707
[57be490]708def getOrientationParam(kernel_module=None):
709    """
710    Get the dictionary with orientation parameters
711    """
712    param = []
[8e2cd79]713    if kernel_module is None:
[57be490]714        return None
715    for param_name in list(kernel_module.params.keys()):
716        name = param_name
717        value = kernel_module.params[param_name]
718        min_state = True
719        max_state = True
720        error_state = False
721        error_value = 0.0
722        checkbox_state = True #??
723        details = kernel_module.details[param_name] #[unit, mix, max]
724        param.append([checkbox_state, name, value, "",
725                     [error_state, error_value],
726                     [min_state, details[1]],
727                     [max_state, details[2]], details[0]])
728
729    return param
[8e2cd79]730
731def formatParameters(parameters):
732    """
733    Prepare the parameter string in the standard SasView layout
734    """
735    assert parameters is not None
736    assert isinstance(parameters, list)
737    output_string = "sasview_parameter_values:"
738    for parameter in parameters:
[b38c8c8]739        # recast tuples into strings
740        parameter = [str(p) for p in parameter]
[8e2cd79]741        output_string += ",".join([p for p in parameter if p is not None])
742        output_string += ":"
743    return output_string
744
745def formatParametersExcel(parameters):
746    """
747    Prepare the parameter string in the Excel format (tab delimited)
748    """
749    assert parameters is not None
750    assert isinstance(parameters, list)
751    crlf = chr(13) + chr(10)
752    tab = chr(9)
753
754    output_string = ""
755    # names
756    names = ""
757    values = ""
758    for parameter in parameters:
759        names += parameter[0]+tab
760        # Add the error column if fitted
761        if parameter[1] == "True" and parameter[3] is not None:
762            names += parameter[0]+"_err"+tab
763
764        values += parameter[2]+tab
765        if parameter[1] == "True" and parameter[3] is not None:
766            values += parameter[3]+tab
767        # add .npts and .nsigmas when necessary
768        if parameter[0][-6:] == ".width":
769            names += parameter[0].replace('.width', '.nsigmas') + tab
770            names += parameter[0].replace('.width', '.npts') + tab
771            values += parameter[5] + tab + parameter[4] + tab
772
773    output_string = names + crlf + values
774    return output_string
775
776def formatParametersLatex(parameters):
777    """
778    Prepare the parameter string in latex
779    """
780    assert parameters is not None
781    assert isinstance(parameters, list)
782    output_string = r'\begin{table}'
783    output_string += r'\begin{tabular}[h]'
784
785    crlf = chr(13) + chr(10)
786    output_string += '{|'
787    output_string += 'l|l|'*len(parameters)
788    output_string += r'}\hline'
789    output_string += crlf
790
791    for index, parameter in enumerate(parameters):
792        name = parameter[0] # Parameter name
793        output_string += name.replace('_', r'\_')  # Escape underscores
794        # Add the error column if fitted
795        if parameter[1] == "True" and parameter[3] is not None:
796            output_string += ' & '
797            output_string += parameter[0]+r'\_err'
798
799        if index < len(parameters) - 1:
800            output_string += ' & '
801
802        # add .npts and .nsigmas when necessary
803        if parameter[0][-6:] == ".width":
804            output_string += parameter[0].replace('.width', '.nsigmas') + ' & '
805            output_string += parameter[0].replace('.width', '.npts')
806
807            if index < len(parameters) - 1:
808                output_string += ' & '
809
810    output_string += r'\\ \hline'
811    output_string += crlf
812
813    # Construct row of values and errors
814    for index, parameter in enumerate(parameters):
815        output_string += parameter[2]
816        if parameter[1] == "True" and parameter[3] is not None:
817            output_string += ' & '
818            output_string += parameter[3]
819
820        if index < len(parameters) - 1:
821            output_string += ' & '
822
823        # add .npts and .nsigmas when necessary
824        if parameter[0][-6:] == ".width":
825            output_string += parameter[5] + ' & '
826            output_string += parameter[4]
827
828            if index < len(parameters) - 1:
829                output_string += ' & '
830
831    output_string += r'\\ \hline'
832    output_string += crlf
833    output_string += r'\end{tabular}'
834    output_string += r'\end{table}'
835
836    return output_string
[305114c]837
838def isParamPolydisperse(param_name, kernel_params, is2D=False):
839    """
840    Simple lookup for polydispersity for the given param name
841    """
842    parameters = kernel_params.form_volume_parameters
843    if is2D:
844        parameters += kernel_params.orientation_parameters
845    has_poly = False
846    for param in parameters:
847        if param.name==param_name and param.polydisperse:
848            has_poly = True
849            break
850    return has_poly
851
Note: See TracBrowser for help on using the repository browser.