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

ESS_GUIESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalc
Last change on this file since 01b4877 was 01b4877, checked in by Torin Cooper-Bennun <torin.cooper-bennun@…>, 6 years ago

Merge branch 'ESS_GUI' into ESS_GUI_iss1034

  • Property mode set to 100644
File size: 26.7 KB
Line 
1import copy
2
3from PyQt5 import QtCore
4from PyQt5 import QtGui
5
6import numpy
7
8from sas.qtgui.Plotting.PlotterData import Data1D
9from sas.qtgui.Plotting.PlotterData import Data2D
10
11from sas.qtgui.Perspectives.Fitting.AssociatedComboBox import AssociatedComboBox
12
13model_header_captions = ['Parameter', 'Value', 'Min', 'Max', 'Units']
14
15model_header_tooltips = ['Select parameter for fitting',
16                         'Enter parameter value',
17                         'Enter minimum value for parameter',
18                         'Enter maximum value for parameter',
19                         'Unit of the parameter']
20
21poly_header_captions = ['Parameter', 'PD[ratio]', 'Min', 'Max', 'Npts', 'Nsigs',
22                        'Function', 'Filename']
23
24poly_header_tooltips = ['Select parameter for fitting',
25                        'Enter polydispersity ratio (STD/mean). '
26                        'STD: standard deviation from the mean value',
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']
33
34error_tooltip = 'Error value for fitted parameter'
35header_error_caption = 'Error'
36
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    """
48    return list([par for par in model.iq_parameters if "[" in par.name])
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)
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)
64
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.
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
93def 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.
98    """
99    multishell_parameters = getIterParams(parameters)
100    multishell_param_name, _ = getMultiplicity(parameters)
101
102    if is2D:
103        params = [p for p in parameters.kernel_parameters if p.type != 'magnetic']
104    else:
105        params = parameters.iq_parameters
106
107    rows = []
108    for param in params:
109        # don't include shell parameters
110        if param.name == multishell_param_name:
111            continue
112
113        # Modify parameter name from <param>[n] to <param>1
114        item_name = param.name
115        if param in multishell_parameters:
116            continue
117
118        item1 = QtGui.QStandardItem(item_name)
119        item1.setCheckable(True)
120        item1.setEditable(False)
121
122        # check for polydisp params
123        if param.polydisperse:
124            poly_item = QtGui.QStandardItem("Polydispersity")
125            poly_item.setEditable(False)
126            item1_1 = QtGui.QStandardItem("Distribution")
127            item1_1.setEditable(False)
128
129            # Find param in volume_params
130            for p in parameters.form_volume_parameters:
131                if p.name != param.name:
132                    continue
133                width = kernel_module.getParam(p.name+'.width')
134                ptype = kernel_module.getParam(p.name+'.type')
135                item1_2 = QtGui.QStandardItem(str(width))
136                item1_2.setEditable(False)
137                item1_3 = QtGui.QStandardItem()
138                item1_3.setEditable(False)
139                item1_4 = QtGui.QStandardItem()
140                item1_4.setEditable(False)
141                item1_5 = QtGui.QStandardItem(ptype)
142                item1_5.setEditable(False)
143                poly_item.appendRow([item1_1, item1_2, item1_3, item1_4, item1_5])
144                break
145
146            # Add the polydisp item as a child
147            item1.appendRow([poly_item])
148
149        # Param values
150        item2 = QtGui.QStandardItem(str(param.default))
151        item3 = QtGui.QStandardItem(str(param.limits[0]))
152        item4 = QtGui.QStandardItem(str(param.limits[1]))
153        item5 = QtGui.QStandardItem(str(param.units))
154        item5.setEditable(False)
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
169def addSimpleParametersToModel(parameters, is2D, parameters_original=None, model=None, view=None, row_num=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)
177    """
178    if is2D:
179        params = [p for p in parameters.kernel_parameters if p.type != 'magnetic']
180    else:
181        params = parameters.iq_parameters
182
183    if parameters_original:
184        # 'parameters_original' contains the parameters as they are to be DISPLAYED, while 'parameters'
185        # contains the parameters as they were renamed; this is for handling name collisions in product model.
186        # The 'real name' of the parameter will be stored in the item's user data.
187        if is2D:
188            params_orig = [p for p in parameters_original.kernel_parameters if p.type != 'magnetic']
189        else:
190            params_orig = parameters_original.iq_parameters
191    else:
192        # no difference in names anyway
193        params_orig = params
194
195    rows = []
196    for param, param_orig in zip(params, params_orig):
197        # Create the top level, checkable item
198        item_name = param_orig.name
199        item1 = QtGui.QStandardItem(item_name)
200        item1.setData(param.name, QtCore.Qt.UserRole)
201        item1.setCheckable(True)
202        item1.setEditable(False)
203
204        # Param values
205        # TODO: add delegate for validation of cells
206        item2 = QtGui.QStandardItem(str(param.default))
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            if row_num is None:
219                model.appendRow(row)
220            else:
221                model.insertRow(row_num, row)
222                row_num += 1
223
224            if cbox:
225                view.setIndexWidget(item2.index(), cbox)
226
227        rows.append(row)
228
229    return rows
230
231def markParameterDisabled(model, row):
232    """Given the QModel row number, format to show it is not available for fitting"""
233
234    # If an error column is present, there are a total of 6 columns.
235    items = [model.item(row, c) for c in range(6)]
236
237    model.blockSignals(True)
238
239    for item in items:
240        if item is None:
241            continue
242        item.setEditable(False)
243        item.setCheckable(False)
244
245    item = items[0]
246
247    font = QtGui.QFont()
248    font.setItalic(True)
249    item.setFont(font)
250    item.setForeground(QtGui.QBrush(QtGui.QColor(100, 100, 100)))
251    item.setToolTip("This parameter cannot be fitted.")
252
253    model.blockSignals(False)
254
255def addCheckedListToModel(model, param_list):
256    """
257    Add a QItem to model. Makes the QItem checkable
258    """
259    assert isinstance(model, QtGui.QStandardItemModel)
260    item_list = [QtGui.QStandardItem(item) for item in param_list]
261    item_list[0].setCheckable(True)
262    model.appendRow(item_list)
263
264def addHeadingRowToModel(model, name):
265    """adds a non-interactive top-level row to the model"""
266    header_row = [QtGui.QStandardItem() for i in range(5)]
267    header_row[0].setText(name)
268
269    font = header_row[0].font()
270    font.setBold(True)
271    header_row[0].setFont(font)
272
273    for item in header_row:
274        item.setEditable(False)
275        item.setCheckable(False)
276        item.setSelectable(False)
277
278    model.appendRow(header_row)
279
280def addHeadersToModel(model):
281    """
282    Adds predefined headers to the model
283    """
284    for i, item in enumerate(model_header_captions):
285        model.setHeaderData(i, QtCore.Qt.Horizontal, item)
286
287    model.header_tooltips = copy.copy(model_header_tooltips)
288
289def addErrorHeadersToModel(model):
290    """
291    Adds predefined headers to the model
292    """
293    model_header_error_captions = copy.copy(model_header_captions)
294    model_header_error_captions.insert(2, header_error_caption)
295    for i, item in enumerate(model_header_error_captions):
296        model.setHeaderData(i, QtCore.Qt.Horizontal, item)
297
298    model_header_error_tooltips = copy.copy(model_header_tooltips)
299    model_header_error_tooltips.insert(2, error_tooltip)
300    model.header_tooltips = copy.copy(model_header_error_tooltips)
301
302def addPolyHeadersToModel(model):
303    """
304    Adds predefined headers to the model
305    """
306    for i, item in enumerate(poly_header_captions):
307        model.setHeaderData(i, QtCore.Qt.Horizontal, item)
308
309    model.header_tooltips = copy.copy(poly_header_tooltips)
310
311
312def addErrorPolyHeadersToModel(model):
313    """
314    Adds predefined headers to the model
315    """
316    poly_header_error_captions = copy.copy(poly_header_captions)
317    poly_header_error_captions.insert(2, header_error_caption)
318    for i, item in enumerate(poly_header_error_captions):
319        model.setHeaderData(i, QtCore.Qt.Horizontal, item)
320
321    poly_header_error_tooltips = copy.copy(poly_header_tooltips)
322    poly_header_error_tooltips.insert(2, error_tooltip)
323    model.header_tooltips = copy.copy(poly_header_error_tooltips)
324
325def addShellsToModel(parameters, model, index, row_num=None, view=None):
326    """
327    Find out multishell parameters and update the model with the requested number of them.
328    Inserts them after the row at row_num, if not None; otherwise, appends to end.
329    If view param is not None, supports fixed-choice params.
330    Returns a list of lists of QStandardItem objects.
331    """
332    multishell_parameters = getIterParams(parameters)
333
334    rows = []
335    for i in range(index):
336        for par in multishell_parameters:
337            # Create the name: <param>[<i>], e.g. "sld1" for parameter "sld[n]"
338            param_name = replaceShellName(par.name, i+1)
339            item1 = QtGui.QStandardItem(param_name)
340            item1.setCheckable(True)
341            # check for polydisp params
342            if par.polydisperse:
343                poly_item = QtGui.QStandardItem("Polydispersity")
344                item1_1 = QtGui.QStandardItem("Distribution")
345                # Find param in volume_params
346                for p in parameters.form_volume_parameters:
347                    if p.name != par.name:
348                        continue
349                    item1_2 = QtGui.QStandardItem(str(p.default))
350                    item1_3 = QtGui.QStandardItem(str(p.limits[0]))
351                    item1_4 = QtGui.QStandardItem(str(p.limits[1]))
352                    item1_5 = QtGui.QStandardItem(str(p.units))
353                    poly_item.appendRow([item1_1, item1_2, item1_3, item1_4, item1_5])
354                    break
355                item1.appendRow([poly_item])
356
357            item2 = QtGui.QStandardItem(str(par.default))
358            item3 = QtGui.QStandardItem(str(par.limits[0]))
359            item4 = QtGui.QStandardItem(str(par.limits[1]))
360            item5 = QtGui.QStandardItem(str(par.units))
361            item5.setEditable(False)
362
363            # Check if fixed-choice (returns combobox, if so, also makes some items uneditable)
364            row = [item1, item2, item3, item4, item5]
365            cbox = createFixedChoiceComboBox(par, row)
366
367            # Always add to the model
368            if row_num is None:
369                model.appendRow(row)
370            else:
371                model.insertRow(row_num, row)
372                row_num += 1
373
374            # Apply combobox if required
375            if None not in (view, cbox):
376                view.setIndexWidget(item2.index(), cbox)
377
378            rows.append(row)
379
380    return rows
381
382def calculateChi2(reference_data, current_data):
383    """
384    Calculate Chi2 value between two sets of data
385    """
386    if reference_data is None or current_data is None:
387        return None
388    # WEIGHING INPUT
389    #from sas.sasgui.perspectives.fitting.utils import get_weight
390    #flag = self.get_weight_flag()
391    #weight = get_weight(data=self.data, is2d=self._is_2D(), flag=flag)
392    chisqr = None
393    if reference_data is None:
394        return chisqr
395
396    # temporary default values for index and weight
397    index = None
398    weight = None
399
400    # Get data: data I, theory I, and data dI in order
401    if isinstance(reference_data, Data2D):
402        if index is None:
403            index = numpy.ones(len(current_data.data), dtype=bool)
404        if weight is not None:
405            current_data.err_data = weight
406        # get rid of zero error points
407        index = index & (current_data.err_data != 0)
408        index = index & (numpy.isfinite(current_data.data))
409        fn = current_data.data[index]
410        gn = reference_data.data[index]
411        en = current_data.err_data[index]
412    else:
413        # 1 d theory from model_thread is only in the range of index
414        if index is None:
415            index = numpy.ones(len(current_data.y), dtype=bool)
416        if weight is not None:
417            current_data.dy = weight
418        if current_data.dy is None or current_data.dy == []:
419            dy = numpy.ones(len(current_data.y))
420        else:
421            ## Set consistently w/AbstractFitengine:
422            # But this should be corrected later.
423            dy = copy.deepcopy(current_data.dy)
424            dy[dy == 0] = 1
425        fn = current_data.y[index]
426        gn = reference_data.y
427        en = dy[index]
428    # Calculate the residual
429    try:
430        res = (fn - gn) / en
431    except ValueError:
432        #print "Chi2 calculations: Unmatched lengths %s, %s, %s" % (len(fn), len(gn), len(en))
433        return None
434
435    residuals = res[numpy.isfinite(res)]
436    chisqr = numpy.average(residuals * residuals)
437
438    return chisqr
439
440def residualsData1D(reference_data, current_data):
441    """
442    Calculate the residuals for difference of two Data1D sets
443    """
444    # temporary default values for index and weight
445    index = None
446    weight = None
447
448    # 1d theory from model_thread is only in the range of index
449    if current_data.dy is None or current_data.dy == []:
450        dy = numpy.ones(len(current_data.y))
451    else:
452        dy = weight if weight is not None else numpy.ones(len(current_data.y))
453        dy[dy == 0] = 1
454    fn = current_data.y[index][0]
455    gn = reference_data.y
456    en = dy[index][0]
457
458    # x values
459    x_current = current_data.x
460    x_reference = reference_data.x
461
462    # build residuals
463    residuals = Data1D()
464    if len(fn) == len(gn):
465        y = (fn - gn)/en
466        residuals.y = -y
467    elif len(fn) > len(gn):
468        residuals.y = (fn - gn[1:len(fn)])/en
469    else:
470        try:
471            y = numpy.zeros(len(current_data.y))
472            begin = 0
473            for i, x_value in enumerate(x_reference):
474                if x_value in x_current:
475                    begin = i
476                    break
477            end = len(x_reference)
478            endl = 0
479            for i, x_value in enumerate(list(x_reference)[::-1]):
480                if x_value in x_current:
481                    endl = i
482                    break
483            # make sure we have correct lengths
484            assert len(x_current) == len(x_reference[begin:end-endl])
485
486            y = (fn - gn[begin:end-endl])/en
487            residuals.y = y
488        except ValueError:
489            # value errors may show up every once in a while for malformed columns,
490            # just reuse what's there already
491            pass
492
493    residuals.x = current_data.x[index][0]
494    residuals.dy = numpy.ones(len(residuals.y))
495    residuals.dx = None
496    residuals.dxl = None
497    residuals.dxw = None
498    residuals.ytransform = 'y'
499    # For latter scale changes
500    residuals.xaxis('\\rm{Q} ', 'A^{-1}')
501    residuals.yaxis('\\rm{Residuals} ', 'normalized')
502
503    return residuals
504
505def residualsData2D(reference_data, current_data):
506    """
507    Calculate the residuals for difference of two Data2D sets
508    """
509    # temporary default values for index and weight
510    # index = None
511    weight = None
512
513    # build residuals
514    residuals = Data2D()
515    # Not for trunk the line below, instead use the line above
516    current_data.clone_without_data(len(current_data.data), residuals)
517    residuals.data = None
518    fn = current_data.data
519    gn = reference_data.data
520    en = current_data.err_data if weight is None else weight
521    residuals.data = (fn - gn) / en
522    residuals.qx_data = current_data.qx_data
523    residuals.qy_data = current_data.qy_data
524    residuals.q_data = current_data.q_data
525    residuals.err_data = numpy.ones(len(residuals.data))
526    residuals.xmin = min(residuals.qx_data)
527    residuals.xmax = max(residuals.qx_data)
528    residuals.ymin = min(residuals.qy_data)
529    residuals.ymax = max(residuals.qy_data)
530    residuals.q_data = current_data.q_data
531    residuals.mask = current_data.mask
532    residuals.scale = 'linear'
533    # check the lengths
534    if len(residuals.data) != len(residuals.q_data):
535        return None
536    return residuals
537
538def plotResiduals(reference_data, current_data):
539    """
540    Create Data1D/Data2D with residuals, ready for plotting
541    """
542    data_copy = copy.deepcopy(current_data)
543    # Get data: data I, theory I, and data dI in order
544    method_name = current_data.__class__.__name__
545    residuals_dict = {"Data1D": residualsData1D,
546                      "Data2D": residualsData2D}
547
548    residuals = residuals_dict[method_name](reference_data, data_copy)
549
550    theory_name = str(current_data.name.split()[0])
551    res_name = reference_data.filename if reference_data.filename else reference_data.name
552    residuals.name = "Residuals for " + str(theory_name) + "[" + res_name + "]"
553    residuals.title = residuals.name
554    residuals.ytransform = 'y'
555
556    # when 2 data have the same id override the 1 st plotted
557    # include the last part if keeping charts for separate models is required
558    residuals.id = "res" + str(reference_data.id) # + str(theory_name)
559    # group_id specify on which panel to plot this data
560    group_id = reference_data.group_id
561    residuals.group_id = "res" + str(group_id)
562
563    # Symbol
564    residuals.symbol = 0
565    residuals.hide_error = False
566
567    return residuals
568
569def binary_encode(i, digits):
570    return [i >> d & 1 for d in range(digits)]
571
572def getWeight(data, is2d, flag=None):
573    """
574    Received flag and compute error on data.
575    :param flag: flag to transform error of data.
576    """
577    weight = None
578    if data is None:
579        return []
580    if is2d:
581        if not hasattr(data, 'err_data'):
582            return []
583        dy_data = data.err_data
584        data = data.data
585    else:
586        if not hasattr(data, 'dy'):
587            return []
588        dy_data = data.dy
589        data = data.y
590
591    if flag == 0:
592        weight = numpy.ones_like(data)
593    elif flag == 1:
594        weight = dy_data
595    elif flag == 2:
596        weight = numpy.sqrt(numpy.abs(data))
597    elif flag == 3:
598        weight = numpy.abs(data)
599    return weight
600
601def updateKernelWithResults(kernel, results):
602    """
603    Takes model kernel and applies results dict to its parameters,
604    returning the modified (deep) copy of the kernel.
605    """
606    assert isinstance(results, dict)
607    local_kernel = copy.deepcopy(kernel)
608
609    for parameter in results.keys():
610        # Update the parameter value - note: this supports +/-inf as well
611        local_kernel.setParam(parameter, results[parameter][0])
612
613    return local_kernel
614
615
616def getStandardParam(model=None):
617    """
618    Returns a list with standard parameters for the current model
619    """
620    param = []
621    num_rows = model.rowCount()
622    if num_rows < 1:
623        return None
624
625    for row in range(num_rows):
626        param_name = model.item(row, 0).text()
627        checkbox_state = model.item(row, 0).checkState() == QtCore.Qt.Checked
628        value = model.item(row, 1).text()
629        column_shift = 0
630        if model.columnCount() == 5: # no error column
631            error_state = False
632            error_value = 0.0
633        else:
634            error_state = True
635            error_value = model.item(row, 2).text()
636            column_shift = 1
637        min_state = True
638        max_state = True
639        min_value = model.item(row, 2+column_shift).text()
640        max_value = model.item(row, 3+column_shift).text()
641        unit = ""
642        if model.item(row, 4+column_shift) is not None:
643            unit = model.item(row, 4+column_shift).text()
644
645        param.append([checkbox_state, param_name, value, "",
646                        [error_state, error_value],
647                        [min_state, min_value],
648                        [max_state, max_value], unit])
649
650    return param
651
652def getOrientationParam(kernel_module=None):
653    """
654    Get the dictionary with orientation parameters
655    """
656    param = []
657    if kernel_module is None:
658        return None
659    for param_name in list(kernel_module.params.keys()):
660        name = param_name
661        value = kernel_module.params[param_name]
662        min_state = True
663        max_state = True
664        error_state = False
665        error_value = 0.0
666        checkbox_state = True #??
667        details = kernel_module.details[param_name] #[unit, mix, max]
668        param.append([checkbox_state, name, value, "",
669                     [error_state, error_value],
670                     [min_state, details[1]],
671                     [max_state, details[2]], details[0]])
672
673    return param
674
675def formatParameters(parameters):
676    """
677    Prepare the parameter string in the standard SasView layout
678    """
679    assert parameters is not None
680    assert isinstance(parameters, list)
681    output_string = "sasview_parameter_values:"
682    for parameter in parameters:
683        output_string += ",".join([p for p in parameter if p is not None])
684        output_string += ":"
685    return output_string
686
687def formatParametersExcel(parameters):
688    """
689    Prepare the parameter string in the Excel format (tab delimited)
690    """
691    assert parameters is not None
692    assert isinstance(parameters, list)
693    crlf = chr(13) + chr(10)
694    tab = chr(9)
695
696    output_string = ""
697    # names
698    names = ""
699    values = ""
700    for parameter in parameters:
701        names += parameter[0]+tab
702        # Add the error column if fitted
703        if parameter[1] == "True" and parameter[3] is not None:
704            names += parameter[0]+"_err"+tab
705
706        values += parameter[2]+tab
707        if parameter[1] == "True" and parameter[3] is not None:
708            values += parameter[3]+tab
709        # add .npts and .nsigmas when necessary
710        if parameter[0][-6:] == ".width":
711            names += parameter[0].replace('.width', '.nsigmas') + tab
712            names += parameter[0].replace('.width', '.npts') + tab
713            values += parameter[5] + tab + parameter[4] + tab
714
715    output_string = names + crlf + values
716    return output_string
717
718def formatParametersLatex(parameters):
719    """
720    Prepare the parameter string in latex
721    """
722    assert parameters is not None
723    assert isinstance(parameters, list)
724    output_string = r'\begin{table}'
725    output_string += r'\begin{tabular}[h]'
726
727    crlf = chr(13) + chr(10)
728    output_string += '{|'
729    output_string += 'l|l|'*len(parameters)
730    output_string += r'}\hline'
731    output_string += crlf
732
733    for index, parameter in enumerate(parameters):
734        name = parameter[0] # Parameter name
735        output_string += name.replace('_', r'\_')  # Escape underscores
736        # Add the error column if fitted
737        if parameter[1] == "True" and parameter[3] is not None:
738            output_string += ' & '
739            output_string += parameter[0]+r'\_err'
740
741        if index < len(parameters) - 1:
742            output_string += ' & '
743
744        # add .npts and .nsigmas when necessary
745        if parameter[0][-6:] == ".width":
746            output_string += parameter[0].replace('.width', '.nsigmas') + ' & '
747            output_string += parameter[0].replace('.width', '.npts')
748
749            if index < len(parameters) - 1:
750                output_string += ' & '
751
752    output_string += r'\\ \hline'
753    output_string += crlf
754
755    # Construct row of values and errors
756    for index, parameter in enumerate(parameters):
757        output_string += parameter[2]
758        if parameter[1] == "True" and parameter[3] is not None:
759            output_string += ' & '
760            output_string += parameter[3]
761
762        if index < len(parameters) - 1:
763            output_string += ' & '
764
765        # add .npts and .nsigmas when necessary
766        if parameter[0][-6:] == ".width":
767            output_string += parameter[5] + ' & '
768            output_string += parameter[4]
769
770            if index < len(parameters) - 1:
771                output_string += ' & '
772
773    output_string += r'\\ \hline'
774    output_string += crlf
775    output_string += r'\end{tabular}'
776    output_string += r'\end{table}'
777
778    return output_string
Note: See TracBrowser for help on using the repository browser.