source: sasview/src/sas/sasgui/plottools/fitDialog.py @ 5251ec6

magnetic_scattrelease-4.2.2ticket-1009ticket-1249
Last change on this file since 5251ec6 was 5251ec6, checked in by Paul Kienzle <pkienzle@…>, 6 years ago

improved support for py37 in sasgui

  • Property mode set to 100644
File size: 30.5 KB
Line 
1import sys
2import math
3
4import wx
5import numpy as np
6
7from . import fittings
8from . import transform
9from .plottables import Theory1D
10
11# Linear fit panel size
12if sys.platform.count("win32") > 0:
13    FONT_VARIANT = 0
14    PNL_WIDTH = 450
15    PNL_HEIGHT = 500
16else:
17    FONT_VARIANT = 1
18    PNL_WIDTH = 500
19    PNL_HEIGHT = 500
20RG_ON = True
21
22def format_number(value, high=False):
23    """
24    Return a float in a standardized, human-readable formatted string.
25    This is used to output readable (e.g. x.xxxe-y) values to the panel.
26    """
27    try:
28        value = float(value)
29    except:
30        output = "NaN"
31        return output.lstrip().rstrip()
32
33    if high:
34        output = "%-6.4g" % value
35
36    else:
37        output = "%-5.3g" % value
38    return output.lstrip().rstrip()
39
40
41class LinearFit(wx.Dialog):
42    def __init__(self, parent, plottable, push_data, transform, title):
43        """
44        Dialog window pops- up when select Linear fit on Context menu
45        Displays fitting parameters. This class handles the linearized
46        fitting and derives and displays specialized output parameters based
47        on the scale choice of the plot calling it.
48
49        :note1: The fitting is currently a bit convoluted as besides using
50        plottools.transform.py to handle all the conversions, it uses
51        LineModel to define a linear model and calculate a number of
52        things like residuals etc as well as the function itself given an x
53        value. It also uses fittings.py to set up the defined LineModel for
54        fitting and then send it to the SciPy NLLSQ method.  As these are by
55        definition "linear nodels" it would make more sense to just call
56        a linear solver such as scipy.stats.linregress or bumps.wsolve directly.
57        This would considerably simplify the code and remove the need I think
58        for LineModel.py and possibly fittins.py altogether.   -PDB 7/10/16
59
60        :note2: The linearized fits do not take resolution into account. This
61        means that for poor resolution such as slit smearing the answers will
62        be completely wrong --- Rg would be OK but I0 would be orders of
63        magnitude off.  Eventually we should fix this to account properly for
64        resolution.   -PDB  7/10/16
65        """
66        wx.Dialog.__init__(self, parent, title=title,
67                           size=(PNL_WIDTH, 350))
68        self.parent = parent
69        self.transform = transform
70        # Font
71        self.SetWindowVariant(variant=FONT_VARIANT)
72        # Registered owner for close event
73        self._registered_close = None
74        # dialog panel self call function to plot the fitting function
75        # calls the calling PlotPanel method onFitDisplay
76        self.push_data = push_data
77        # dialog self plottable - basically the plot we are working with
78        # passed in by the caller
79        self.plottable = plottable
80        # is this a Guinier fit
81        self.rg_on = False
82        # Receive transformations of x and y - basically transform is passed
83        # as caller method that returns its current value for these
84        self.xLabel, self.yLabel, self.Avalue, self.Bvalue, \
85               self.ErrAvalue, self.ErrBvalue, self.Chivalue = self.transform()
86
87        # Now set up the dialog interface
88        self.layout()
89        # Receives the type of model for the fitting
90        from .LineModel import LineModel
91        self.model = LineModel()
92        # Display the fittings values
93        self.default_A = self.model.getParam('A')
94        self.default_B = self.model.getParam('B')
95        self.cstA = fittings.Parameter(self.model, 'A', self.default_A)
96        self.cstB = fittings.Parameter(self.model, 'B', self.default_B)
97
98        # Set default value of parameter in the dialog panel
99        if self.Avalue is None:
100            self.tcA.SetValue(format_number(self.default_A))
101        else:
102            self.tcA.SetLabel(format_number(self.Avalue))
103        if self.Bvalue is None:
104            self.tcB.SetValue(format_number(self.default_B))
105        else:
106            self.tcB.SetLabel(format_number(self.Bvalue))
107        if self.ErrAvalue is None:
108            self.tcErrA.SetLabel(format_number(0.0))
109        else:
110            self.tcErrA.SetLabel(format_number(self.ErrAvalue))
111        if self.ErrBvalue is None:
112            self.tcErrB.SetLabel(format_number(0.0))
113        else:
114            self.tcErrB.SetLabel(format_number(self.ErrBvalue))
115        if self.Chivalue is None:
116            self.tcChi.SetLabel(format_number(0.0))
117        else:
118            self.tcChi.SetLabel(format_number(self.Chivalue))
119        if self.plottable.x != []:
120            # store the values of View in self.x,self.y,self.dx,self.dy
121            self.x, self.y, self.dx, \
122                     self.dy = self.plottable.returnValuesOfView()
123            try:
124                self.mini = self.floatForwardTransform(min(self.x))
125            except:
126                self.mini = "Invalid"
127            try:
128                self.maxi = self.floatForwardTransform(max(self.x))
129            except:
130                self.maxi = "Invalid"
131
132            self.initXmin.SetValue(format_number(min(self.plottable.x)))
133            self.initXmax.SetValue(format_number(max(self.plottable.x)))
134            self.mini = min(self.x)
135            self.maxi = max(self.x)
136            self.xminFit.SetValue(format_number(self.mini))
137            self.xmaxFit.SetValue(format_number(self.maxi))
138
139    def layout(self):
140        """
141        Sets up the panel layout for the linear fit including all the
142        labels, text entry boxes, and buttons.
143
144        """
145
146        # set up sizers first.
147        # vbox is the panel sizer and is a vertical sizer
148        # The first element of the panel is sizer which is a gridbagsizer
149        # and contains most of the text fields
150        # this is followed by a line separator added to vbox
151        # and finally the sizer_button (a horizontal sizer) adds the buttons
152        vbox = wx.BoxSizer(wx.VERTICAL)
153        sizer = wx.GridBagSizer(5, 5)
154        sizer_button = wx.BoxSizer(wx.HORIZONTAL)
155
156        #size of string boxes in pixels
157        _BOX_WIDTH = 100
158        _BOX_HEIGHT = 20
159        #now set up all the text fields
160        self.tcA = wx.TextCtrl(self, -1, size=(_BOX_WIDTH, _BOX_HEIGHT))
161        self.tcA.SetToolTipString("Fit value for the slope parameter.")
162        self.tcErrA = wx.TextCtrl(self, -1, size=(_BOX_WIDTH, _BOX_HEIGHT))
163        self.tcErrA.SetToolTipString("Error on the slope parameter.")
164        self.tcB = wx.TextCtrl(self, -1, size=(_BOX_WIDTH, _BOX_HEIGHT))
165        self.tcA.SetToolTipString("Fit value for the constant parameter.")
166        self.tcErrB = wx.TextCtrl(self, -1, size=(_BOX_WIDTH, _BOX_HEIGHT))
167        self.tcErrB.SetToolTipString("Error on the constant parameter.")
168        self.tcChi = wx.TextCtrl(self, -1, size=(_BOX_WIDTH, _BOX_HEIGHT))
169        self.tcChi.SetToolTipString("Chi^2 over degrees of freedom.")
170        self.xminFit = wx.TextCtrl(self, -1, size=(_BOX_WIDTH, _BOX_HEIGHT))
171        msg = "Enter the minimum value on "
172        msg += "the x-axis to be included in the fit."
173        self.xminFit.SetToolTipString(msg)
174        self.xmaxFit = wx.TextCtrl(self, -1, size=(_BOX_WIDTH, _BOX_HEIGHT))
175        msg = "Enter the maximum value on "
176        msg += " the x-axis to be included in the fit."
177        self.xmaxFit.SetToolTipString(msg)
178        self.initXmin = wx.TextCtrl(self, -1, size=(_BOX_WIDTH, _BOX_HEIGHT))
179        msg = "Minimum value on the x-axis for the plotted data."
180        self.initXmin.SetToolTipString(msg)
181        self.initXmax = wx.TextCtrl(self, -1, size=(_BOX_WIDTH, _BOX_HEIGHT))
182        msg = "Maximum value on the x-axis for the plotted data."
183        self.initXmax.SetToolTipString(msg)
184
185        # Make the info box not editable
186        # _BACKGROUND_COLOR = '#ffdf85'
187        _BACKGROUND_COLOR = self.GetBackgroundColour()
188        self.initXmin.SetEditable(False)
189        self.initXmin.SetBackgroundColour(_BACKGROUND_COLOR)
190        self.initXmax.SetEditable(False)
191        self.initXmax.SetBackgroundColour(_BACKGROUND_COLOR)
192
193        #set some flags for specific types of fits like Guinier (Rg) and
194        #Porod (bg) -- this will determine WHAT boxes show up in the
195        #sizer layout and depends on the active axis transform
196        self.bg_on = False
197        if RG_ON:
198            if (self.yLabel == "ln(y)" or self.yLabel == "ln(y*x)") and \
199                    (self.xLabel == "x^(2)"):
200                self.rg_on = True
201            if (self.xLabel == "x^(4)") and (self.yLabel == "y*x^(4)"):
202                self.bg_on = True
203
204        # Finally set up static text strings
205        warning = "WARNING! Resolution is NOT accounted for. \n"
206        warning += "Thus slit smeared data will give very wrong answers!"
207        self.textwarn = wx.StaticText(self, -1, warning)
208        self.textwarn.SetForegroundColour(wx.RED)
209        explanation = "Perform fit for y(x) = ax + b \n"
210        if self.bg_on:
211            param_a = 'Background (= Parameter a)'
212        else:
213            param_a = 'Parameter a'
214
215
216        #Now set this all up in the GridBagSizer sizer
217        ix = 0
218        iy = 0
219        sizer.Add(self.textwarn, (iy, ix),
220                  (2, 3), wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
221        iy += 2
222        sizer.Add(wx.StaticText(self, -1, explanation), (iy, ix),
223                  (1, 1), wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
224        iy += 1
225        sizer.Add(wx.StaticText(self, -1, param_a), (iy, ix),
226                  (1, 1), wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
227        ix += 1
228        sizer.Add(self.tcA, (iy, ix), (1, 1), wx.EXPAND | wx.ADJUST_MINSIZE, 0)
229        ix += 1
230        sizer.Add(wx.StaticText(self, -1, '+/-'),
231                  (iy, ix), (1, 1), wx.EXPAND | wx.ADJUST_MINSIZE, 0)
232        ix += 1
233        sizer.Add(self.tcErrA, (iy, ix), (1, 1),
234                  wx.EXPAND | wx.ADJUST_MINSIZE, 0)
235        iy += 1
236        ix = 0
237        sizer.Add(wx.StaticText(self, -1, 'Parameter b'), (iy, ix), (1, 1),
238                  wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
239        ix += 1
240        sizer.Add(self.tcB, (iy, ix), (1, 1), wx.EXPAND | wx.ADJUST_MINSIZE, 0)
241        ix += 1
242        sizer.Add(wx.StaticText(self, -1, '+/-'), (iy, ix),
243                  (1, 1), wx.EXPAND | wx.ADJUST_MINSIZE, 0)
244        ix += 1
245        sizer.Add(self.tcErrB, (iy, ix), (1, 1),
246                  wx.EXPAND | wx.ADJUST_MINSIZE, 0)
247        iy += 1
248        ix = 0
249        sizer.Add(wx.StaticText(self, -1, 'Chi2/dof'), (iy, ix), (1, 1),
250                  wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
251        ix += 1
252        sizer.Add(self.tcChi, (iy, ix), (1, 1), wx.EXPAND | wx.ADJUST_MINSIZE, 0)
253        iy += 2
254        ix = 1
255        sizer.Add(wx.StaticText(self, -1, 'Min'), (iy, ix), (1, 1),
256                  wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 0)
257        ix += 2
258        sizer.Add(wx.StaticText(self, -1, 'Max'), (iy, ix),
259                  (1, 1), wx.EXPAND | wx.ADJUST_MINSIZE, 0)
260
261        iy += 1
262        ix = 0
263        sizer.Add(wx.StaticText(self, -1, 'Maximum range (linear scale)'),
264                  (iy, ix), (1, 1),
265                  wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
266        ix += 1
267        sizer.Add(self.initXmin, (iy, ix), (1, 1),
268                  wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 0)
269        ix += 2
270        sizer.Add(self.initXmax, (iy, ix), (1, 1),
271                  wx.EXPAND | wx.ADJUST_MINSIZE, 0)
272
273        iy += 1
274        ix = 0
275        sizer.Add(wx.StaticText(self, -1, 'Fit range of ' + self.xLabel),
276                  (iy, ix), (1, 1),
277                  wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
278        ix += 1
279        sizer.Add(self.xminFit, (iy, ix), (1, 1),
280                  wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 0)
281        ix += 2
282        sizer.Add(self.xmaxFit, (iy, ix), (1, 1), wx.EXPAND | wx.ADJUST_MINSIZE, 0)
283        if self.rg_on:
284            self.SetSize((PNL_WIDTH, PNL_HEIGHT))
285            I0_stxt = wx.StaticText(self, -1, 'I(q=0)')
286            self.I0_tctr = wx.TextCtrl(self, -1, '')
287            self.I0_tctr.SetEditable(False)
288            self.I0_tctr.SetBackgroundColour(_BACKGROUND_COLOR)
289            self.I0err_tctr = wx.TextCtrl(self, -1, '')
290            self.I0err_tctr.SetEditable(False)
291            self.I0err_tctr.SetBackgroundColour(_BACKGROUND_COLOR)
292            Rg_stxt = wx.StaticText(self, -1, 'Rg [A]')
293            Rg_stxt.Show(self.yLabel == "ln(y)")
294            self.Rg_tctr = wx.TextCtrl(self, -1, '')
295            self.Rg_tctr.SetEditable(False)
296            self.Rg_tctr.SetBackgroundColour(_BACKGROUND_COLOR)
297            self.Rg_tctr.Show(self.yLabel == "ln(y)")
298            self.Rgerr_tctr = wx.TextCtrl(self, -1, '')
299            self.Rgerr_tctr.SetEditable(False)
300            self.Rgerr_tctr.SetBackgroundColour(_BACKGROUND_COLOR)
301            self.Rgerr_tctr.Show(self.yLabel == "ln(y)")
302            self.Rgerr_pm = wx.StaticText(self, -1, '+/-')
303            self.Rgerr_pm.Show(self.yLabel == "ln(y)")
304            Diameter_stxt = wx.StaticText(self, -1, 'Rod Diameter [A]')
305            Diameter_stxt.Show(self.yLabel == "ln(y*x)")
306            self.Diameter_tctr = wx.TextCtrl(self, -1, '')
307            self.Diameter_tctr.SetEditable(False)
308            self.Diameter_tctr.SetBackgroundColour(_BACKGROUND_COLOR)
309            self.Diameter_tctr.Show(self.yLabel == "ln(y*x)")
310            self.Diameter_pm = wx.StaticText(self, -1, '+/-')
311            self.Diameter_pm.Show(self.yLabel == "ln(y*x)")
312            self.Diametererr_tctr = wx.TextCtrl(self, -1, '')
313            self.Diametererr_tctr.SetEditable(False)
314            self.Diametererr_tctr.SetBackgroundColour(_BACKGROUND_COLOR)
315            self.Diametererr_tctr.Show(self.yLabel == "ln(y*x)")
316            RgQmin_stxt = wx.StaticText(self, -1, 'Rg*Qmin')
317            self.RgQmin_tctr = wx.TextCtrl(self, -1, '')
318            self.RgQmin_tctr.SetEditable(False)
319            self.RgQmin_tctr.SetBackgroundColour(_BACKGROUND_COLOR)
320            RgQmax_stxt = wx.StaticText(self, -1, 'Rg*Qmax')
321            self.RgQmax_tctr = wx.TextCtrl(self, -1, '')
322            self.RgQmax_tctr.SetEditable(False)
323            self.RgQmax_tctr.SetBackgroundColour(_BACKGROUND_COLOR)
324
325            iy += 2
326            ix = 0
327            sizer.Add(I0_stxt, (iy, ix), (1, 1),
328                      wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
329            ix += 1
330            sizer.Add(self.I0_tctr, (iy, ix), (1, 1),
331                      wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 0)
332            ix += 1
333            sizer.Add(wx.StaticText(self, -1, '+/-'), (iy, ix),
334                      (1, 1), wx.EXPAND | wx.ADJUST_MINSIZE, 0)
335            ix += 1
336            sizer.Add(self.I0err_tctr, (iy, ix), (1, 1),
337                      wx.EXPAND | wx.ADJUST_MINSIZE, 0)
338
339            iy += 1
340            ix = 0
341            sizer.Add(Rg_stxt, (iy, ix), (1, 1),
342                      wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
343            ix += 1
344            sizer.Add(self.Rg_tctr, (iy, ix), (1, 1),
345                      wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 0)
346
347            ix += 1
348            sizer.Add(self.Rgerr_pm, (iy, ix),
349                      (1, 1), wx.EXPAND | wx.ADJUST_MINSIZE, 0)
350            ix += 1
351            sizer.Add(self.Rgerr_tctr, (iy, ix), (1, 1),
352                      wx.EXPAND | wx.ADJUST_MINSIZE, 0)
353            iy += 1
354            ix = 0
355            sizer.Add(Diameter_stxt, (iy, ix), (1, 1),
356                      wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
357            ix += 1
358            sizer.Add(self.Diameter_tctr, (iy, ix), (1, 1),
359                      wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 0)
360
361            ix += 1
362            sizer.Add(self.Diameter_pm, (iy, ix),
363                      (1, 1), wx.EXPAND | wx.ADJUST_MINSIZE, 0)
364            ix += 1
365            sizer.Add(self.Diametererr_tctr, (iy, ix), (1, 1),
366                      wx.EXPAND | wx.ADJUST_MINSIZE, 0)
367            iy += 1
368            ix = 0
369            sizer.Add(RgQmin_stxt, (iy, ix), (1, 1),
370                      wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
371            ix += 1
372            sizer.Add(self.RgQmin_tctr, (iy, ix), (1, 1),
373                      wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 0)
374            iy += 1
375            ix = 0
376            sizer.Add(RgQmax_stxt, (iy, ix), (1, 1),
377                      wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
378            ix += 1
379            sizer.Add(self.RgQmax_tctr, (iy, ix), (1, 1),
380                      wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 0)
381
382        #Now add some space before the separation line
383        iy += 1
384        ix = 0
385        sizer.Add((20,20), (iy, ix), (1, 1),
386                      wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 0)
387
388        # Buttons on the bottom
389        self.btFit = wx.Button(self, -1, 'Fit')
390        self.btFit.Bind(wx.EVT_BUTTON, self._onFit)
391        self.btFit.SetToolTipString("Perform fit.")
392        self.btClose = wx.Button(self, wx.ID_CANCEL, 'Close')
393        self.btClose.Bind(wx.EVT_BUTTON, self._on_close)
394        sizer_button.Add((20, 20), 1, wx.EXPAND | wx.ADJUST_MINSIZE, 0)
395        sizer_button.Add(self.btFit, 0,
396                         wx.LEFT | wx.RIGHT | wx.ADJUST_MINSIZE, 10)
397        sizer_button.Add(self.btClose, 0,
398                         wx.LEFT | wx.RIGHT | wx.ADJUST_MINSIZE, 10)
399
400        vbox.Add(sizer)
401        self.static_line_1 = wx.StaticLine(self, -1)
402        vbox.Add(self.static_line_1, 0, wx.EXPAND, 0)
403        vbox.Add(sizer_button, 0, wx.EXPAND | wx.BOTTOM | wx.TOP, 10)
404
405        # panel.SetSizer(sizer)
406        self.SetSizer(vbox)
407        self.Centre()
408
409    def register_close(self, owner):
410        """
411        Method to register the close event to a parent
412        window that needs notification when the dialog
413        is closed
414
415        :param owner: parent window
416
417        """
418        self._registered_close = owner
419
420    def _on_close(self, event):
421        """
422        Close event.
423        Notify registered owner if available.
424        """
425        event.Skip()
426        if self._registered_close is not None:
427            self._registered_close()
428
429    def _onFit(self, event):
430        """
431        Performs the fit. Receive an event when clicking on
432        the button Fit.Computes chisqr ,
433        A and B parameters of the best linear fit y=Ax +B
434        Push a plottable to the caller
435        """
436        tempx = []
437        tempy = []
438        tempdy = []
439
440        # Check if View contains a x array .we online fit when x exits
441        # makes transformation for y as a line to fit
442        if self.x != []:
443            if self.checkFitValues(self.xminFit):
444                # Check if the field of Fit Dialog contain values
445                # and use the x max and min of the user
446                if not self._checkVal(self.xminFit, self.xmaxFit):
447                    return
448                xminView = float(self.xminFit.GetValue())
449                xmaxView = float(self.xmaxFit.GetValue())
450                xmin = xminView
451                xmax = xmaxView
452                # Set the qmin and qmax in the panel that matches the
453                # transformed min and max
454                self.initXmin.SetValue(format_number(self.floatInvTransform(xmin)))
455                self.initXmax.SetValue(format_number(self.floatInvTransform(xmax)))
456                # Store the transformed values of view x, y,dy
457                # in variables  before the fit
458                if self.yLabel.lower() == "log10(y)":
459                    if self.xLabel.lower() == "log10(x)":
460                        for i in range(len(self.x)):
461                            if self.x[i] >= math.log10(xmin):
462                                tempy.append(math.log10(self.y[i]))
463                                tempdy.append(transform.errToLogX(self.y[i], 0, self.dy[i], 0))
464                    else:
465                        for i in range(len(self.y)):
466                            tempy.append(math.log10(self.y[i]))
467                            tempdy.append(transform.errToLogX(self.y[i], 0, self.dy[i], 0))
468                else:
469                    tempy = self.y
470                    tempdy = self.dy
471
472                if self.xLabel.lower() == "log10(x)":
473                    for x_i in self.x:
474                        if x_i >= math.log10(xmin):
475                            tempx.append(math.log10(x_i))
476                else:
477                    tempx = self.x
478
479                # Find the fitting parameters
480                # Always use the same defaults, so that fit history
481                # doesn't play a role!
482                self.cstA = fittings.Parameter(self.model, 'A', self.default_A)
483                self.cstB = fittings.Parameter(self.model, 'B', self.default_B)
484
485                if self.xLabel.lower() == "log10(x)":
486                    tempdy = np.asarray(tempdy)
487                    tempdy[tempdy == 0] = 1
488                    chisqr, out, cov = fittings.sasfit(self.model,
489                                                       [self.cstA, self.cstB],
490                                                       tempx, tempy,
491                                                       tempdy,
492                                                       math.log10(xmin),
493                                                       math.log10(xmax))
494                else:
495                    tempdy = np.asarray(tempdy)
496                    tempdy[tempdy == 0] = 1
497                    chisqr, out, cov = fittings.sasfit(self.model,
498                                                       [self.cstA, self.cstB],
499                                                       tempx, tempy, tempdy,
500                                                       xminView, xmaxView)
501                # Use chi2/dof
502                if len(tempx) > 0:
503                    chisqr = chisqr / len(tempx)
504
505                # Check that cov and out are iterable before displaying them
506                if cov is None:
507                    errA = 0.0
508                    errB = 0.0
509                else:
510                    errA = math.sqrt(cov[0][0])
511                    errB = math.sqrt(cov[1][1])
512                if out is None:
513                    cstA = 0.0
514                    cstB = 0.0
515                else:
516                    cstA = out[0]
517                    cstB = out[1]
518                # Reset model with the right values of A and B
519                self.model.setParam('A', float(cstA))
520                self.model.setParam('B', float(cstB))
521
522                tempx = []
523                tempy = []
524                y_model = 0.0
525                # load tempy with the minimum transformation
526
527                if self.xLabel == "log10(x)":
528                    y_model = self.model.run(math.log10(xmin))
529                    tempx.append(xmin)
530                else:
531                    y_model = self.model.run(xminView)
532                    tempx.append(xminView)
533
534                if self.yLabel == "log10(y)":
535                    tempy.append(math.pow(10, y_model))
536                else:
537                    tempy.append(y_model)
538
539                # load tempy with the maximum transformation
540                if self.xLabel == "log10(x)":
541                    y_model = self.model.run(math.log10(xmax))
542                    tempx.append(xmax)
543                else:
544                    y_model = self.model.run(xmaxView)
545                    tempx.append(xmaxView)
546
547                if self.yLabel == "log10(y)":
548                    tempy.append(math.pow(10, y_model))
549                else:
550                    tempy.append(y_model)
551                # Set the fit parameter display when  FitDialog is opened again
552                self.Avalue = cstA
553                self.Bvalue = cstB
554                self.ErrAvalue = errA
555                self.ErrBvalue = errB
556                self.Chivalue = chisqr
557                self.push_data(tempx, tempy, xminView, xmaxView,
558                               xmin, xmax, self._ongetValues())
559
560                # Display the fitting value on the Fit Dialog
561                self._onsetValues(cstA, cstB, errA, errB, chisqr)
562
563    def _onsetValues(self, cstA, cstB, errA, errB, Chi):
564        """
565        Display  the value on fit Dialog
566        """
567        rg = None
568        _diam = None
569        self.tcA.SetValue(format_number(cstA))
570        self.tcB.SetValue(format_number(cstB))
571        self.tcErrA.SetValue(format_number(errA))
572        self.tcErrB.SetValue(format_number(errB))
573        self.tcChi.SetValue(format_number(Chi))
574        if self.rg_on:
575            if self.Rg_tctr.IsShown():
576                rg = np.sqrt(-3 * float(cstA))
577                value = format_number(rg)
578                self.Rg_tctr.SetValue(value)
579                if self.I0_tctr.IsShown():
580                    val = np.exp(cstB)
581                    self.I0_tctr.SetValue(format_number(val))
582            if self.Rgerr_tctr.IsShown():
583                if rg is not None and rg != 0:
584                    value = format_number(3 * float(errA) / (2 * rg))
585                else:
586                    value = ''
587                self.Rgerr_tctr.SetValue(value)
588                if self.I0err_tctr.IsShown():
589                    val = np.abs(np.exp(cstB) * errB)
590                    self.I0err_tctr.SetValue(format_number(val))
591            if self.Diameter_tctr.IsShown():
592                rg = np.sqrt(-2 * float(cstA))
593                _diam = 4 * np.sqrt(-float(cstA))
594                value = format_number(_diam)
595                self.Diameter_tctr.SetValue(value)
596            if self.Diametererr_tctr.IsShown():
597                if rg is not None and rg != 0:
598                    value = format_number(8 * float(errA) / _diam)
599                else:
600                    value = ''
601                self.Diametererr_tctr.SetValue(value)
602            if self.RgQmin_tctr.IsShown():
603                value = format_number(rg * self.floatInvTransform(self.mini))
604                self.RgQmin_tctr.SetValue(value)
605            if self.RgQmax_tctr.IsShown():
606                value = format_number(rg * self.floatInvTransform(self.maxi))
607                self.RgQmax_tctr.SetValue(value)
608
609    def _ongetValues(self):
610        """
611        Display  the value on fit Dialog
612        """
613        return self.Avalue, self.Bvalue, self.ErrAvalue, \
614                            self.ErrBvalue, self.Chivalue
615
616    def _checkVal(self, usermin, usermax):
617        """
618        Ensure that fields parameter contains a min and a max value
619        within x min and x max range
620        """
621        self.mini = float(self.xminFit.GetValue())
622        self.maxi = float(self.xmaxFit.GetValue())
623        flag = True
624        try:
625            mini = float(usermin.GetValue())
626            maxi = float(usermax.GetValue())
627            if mini < maxi:
628                usermin.SetBackgroundColour(wx.WHITE)
629                usermin.Refresh()
630            else:
631                flag = False
632                usermin.SetBackgroundColour("pink")
633                usermin.Refresh()
634        except:
635            # Check for possible values entered
636            flag = False
637            usermin.SetBackgroundColour("pink")
638            usermin.Refresh()
639
640        return flag
641
642    def floatForwardTransform(self, x):
643        """
644        transform a float.
645        """
646        # TODO: refactor this with proper object-oriented design
647        # This code stinks.
648        if self.xLabel == "x":
649            return transform.toX(x)
650        if self.xLabel == "x^(2)":
651            return transform.toX2(x)
652        if self.xLabel == "ln(x)":
653            return transform.toLogX(x)
654        if self.xLabel == "log10(x)":
655            return math.log10(x)
656
657    def floatTransform(self, x):
658        """
659        transform a float.It is use to determine the x.
660        View min and x.View max for values
661        not in x
662        """
663        # TODO: refactor this with proper object-oriented design
664        # This code stinks.
665        if self.xLabel == "x":
666            return transform.toX(x)
667        if self.xLabel == "x^(2)":
668            return transform.toX2(x)
669        if self.xLabel == "ln(x)":
670            return transform.toLogX(x)
671        if self.xLabel == "log10(x)":
672            if x > 0:
673                return x
674            else:
675                raise ValueError("cannot compute log of a negative number")
676
677    def floatInvTransform(self, x):
678        """
679        transform a float.It is used to determine the x.View min and x.View
680        max for values not in x.  Also used to properly calculate RgQmin,
681        RgQmax and to update qmin and qmax in the linear range boxes on the
682        panel.
683
684        """
685        # TODO: refactor this. This is just a hack to make the
686        # functionality work without rewritting the whole code
687        # with good design (which really should be done...).
688        if self.xLabel == "x":
689            return x
690        elif self.xLabel == "x^(2)":
691            return math.sqrt(x)
692        elif self.xLabel == "x^(4)":
693            return math.sqrt(math.sqrt(x))
694        elif self.xLabel == "log10(x)":
695            return math.pow(10, x)
696        elif self.xLabel == "ln(x)":
697            return math.exp(x)
698        elif self.xLabel == "log10(x^(4))":
699            return math.sqrt(math.sqrt(math.pow(10, x)))
700        return x
701
702    def checkFitValues(self, item):
703        """
704            Check the validity of input values
705        """
706        flag = True
707        value = item.GetValue()
708        # Check for possible values entered
709        if self.xLabel == "log10(x)":
710            if float(value) > 0:
711                item.SetBackgroundColour(wx.WHITE)
712                item.Refresh()
713            else:
714                flag = False
715                item.SetBackgroundColour("pink")
716                item.Refresh()
717        return flag
718
719    def setFitRange(self, xmin, xmax, xminTrans, xmaxTrans):
720        """
721        Set fit parameters
722        """
723        self.xminFit.SetValue(format_number(xmin))
724        self.xmaxFit.SetValue(format_number(xmax))
725
726    def set_fit_region(self, xmin, xmax):
727        """
728        Set the fit region
729        :param xmin: minimum x-value to be included in fit
730        :param xmax: maximum x-value to be included in fit
731        """
732        # Check values
733        try:
734            float(xmin)
735            float(xmax)
736        except:
737            msg = "LinearFit.set_fit_region: fit range must be floats"
738            raise ValueError(msg)
739        self.xminFit.SetValue(format_number(xmin))
740        self.xmaxFit.SetValue(format_number(xmax))
741
742
743class MyApp(wx.App):
744    """
745        Test application
746    """
747    def OnInit(self):
748        """
749            Test application initialization
750        """
751        wx.InitAllImageHandlers()
752        plot = Theory1D([], [])
753        dialog = LinearFit(parent=None, plottable=plot,
754                           push_data=self.onFitDisplay,
755                           transform=self.returnTrans,
756                           title='Linear Fit')
757        if dialog.ShowModal() == wx.ID_OK:
758            pass
759        dialog.Destroy()
760        return 1
761
762    def onFitDisplay(self, tempx, tempy, xminView, xmaxView, xmin, xmax, func):
763        """
764            Test application dummy method
765        """
766        pass
767
768    def returnTrans(self):
769        """
770            Test application dummy method
771        """
772        return '', '', 0, 0, 0, 0, 0
Note: See TracBrowser for help on using the repository browser.