Changeset dd5bf63 in sasview for src/sas/sasgui/plottools
- Timestamp:
- Jul 10, 2016 11:40:42 PM (8 years ago)
- Branches:
- master, ESS_GUI, ESS_GUI_Docs, ESS_GUI_batch_fitting, ESS_GUI_bumps_abstraction, ESS_GUI_iss1116, ESS_GUI_iss879, ESS_GUI_iss959, ESS_GUI_opencl, ESS_GUI_ordering, ESS_GUI_sync_sascalc, costrafo411, magnetic_scatt, release-4.1.1, release-4.1.2, release-4.2.2, release_4.0.1, ticket-1009, ticket-1094-headless, ticket-1242-2d-resolution, ticket-1243, ticket-1249, ticket885, unittest-saveload
- Children:
- c23f303, 77d92cd, d398285
- Parents:
- 3409a90
- Location:
- src/sas/sasgui/plottools
- Files:
-
- 4 edited
Legend:
- Unmodified
- Added
- Removed
-
src/sas/sasgui/plottools/LineModel.py
rd7bb526 rdd5bf63 1 1 #!/usr/bin/env python 2 2 """ 3 Provide Line function (y= A + Bx) 3 Provide Line function (y= Ax + B). Until July 10, 2016 this function provided 4 (y= A + Bx). This however was contrary to all the other code using it which 5 assumed (y= mx+b) or in this nomenclature (y=Ax + B). This lead to some 6 contortions in the code and worse incorrect calculations until now for at least 7 some of the functions. This seemed the easiest to fix particularly since this 8 function should disappear in a future iteration (see notes in fitDialog) 9 10 -PDB July 10, 2016 4 11 """ 5 12 … … 10 17 Class that evaluates a linear model. 11 18 12 f(x) = A + Bx19 f(x) = Ax + B 13 20 14 21 List of default parameters: 15 A = 0.016 B = 0.022 A = 1.0 23 B = 1.0 17 24 """ 18 25 … … 53 60 54 61 """ 55 return self.params['A'] + (x * self.params['B'])62 return (self.params['A'] * x) + self.params['B'] 56 63 57 64 def run(self, x=0.0): 58 65 """ 59 66 Evaluate the model 67 68 :note: This is the function called by fitDialog to calculate the 69 the y(xmin) and y(xmax), but the only difference between this and 70 runXY is when the if statement is true. I however cannot see what that 71 function is for. It needs to be documented here or removed. 72 -PDB 7/10/16 60 73 61 74 :param x: simple value … … 74 87 def runXY(self, x=0.0): 75 88 """ 76 Evaluate the model 89 Evaluate the model. 90 91 :note: This is to be what is called by fitDialog for the actual fit 92 but the only difference between this and run is when the if 93 statement is true. I however cannot see what that function 94 is for. It needs to be documented here or removed. -PDB 7/10/16 77 95 78 96 :param x: simple value -
src/sas/sasgui/plottools/PlotPanel.py
r16b769b rdd5bf63 646 646 dlg.setFitRange(self.xminView, self.xmaxView, 647 647 self.xmin, self.xmax) 648 # It would be nice for this to NOT be modal (i.e. Show). 649 # Not sure about other ramifications - for example 650 # if a second linear fit is started before the first is closed. 651 # consider for future - being able to work on the plot while 652 # seing the fit values would be very nice -- PDB 7/10/16 648 653 dlg.ShowModal() 649 654 -
src/sas/sasgui/plottools/fitDialog.py
r7c1d04a rdd5bf63 20 20 def format_number(value, high=False): 21 21 """ 22 Return a float in a standardized, human-readable formatted string 22 Return a float in a standardized, human-readable formatted string. 23 This is used to output readable (e.g. x.xxxe-y) values to the panel. 23 24 """ 24 25 try: … … 40 41 """ 41 42 Dialog window pops- up when select Linear fit on Context menu 42 Displays fitting parameters 43 Displays fitting parameters. This class handles the linearized 44 fitting and derives and displays specialized output parameters based 45 on the scale choice of the plot calling it. 46 47 :note1: The fitting is currently a bit convoluted as besides using 48 plottools.transform.py to handle all the conversions, it uses 49 LineModel to define a linear model and calculate a number of 50 things like residuals etc as well as the function itself given an x 51 value. It also uses fittings.py to set up the defined LineModel for 52 fitting and then send it to the SciPy NLLSQ method. As these are by 53 definition "linear nodels" it would make more sense to just call 54 a linear solver such as scipy.stats.linregress or bumps.wsolve directly. 55 This would considerably simplify the code and remove the need I think 56 for LineModel.py and possibly fittins.py altogether. -PDB 7/10/16 57 58 :note2: The linearized fits do not take resolution into account. This 59 means that for poor resolution such as slit smearing the answers will 60 be completely wrong --- Rg would be OK but I0 would be orders of 61 magnitude off. Eventually we should fix this to account properly for 62 resolution. -PDB 7/10/16 43 63 """ 44 64 wx.Dialog.__init__(self, parent, title=title, … … 50 70 # Registered owner for close event 51 71 self._registered_close = None 52 53 72 # dialog panel self call function to plot the fitting function 73 # calls the calling PlotPanel method onFitDisplay 54 74 self.push_data = push_data 55 # dialog self plottable 75 # dialog self plottable - basically the plot we are working with 76 # passed in by the caller 56 77 self.plottable = plottable 78 # is this a Guinier fit 57 79 self.rg_on = False 58 # Receive transformations of x and y 80 # Receive transformations of x and y - basically transform is passed 81 # as caller method that returns its current value for these 59 82 self.xLabel, self.yLabel, self.Avalue, self.Bvalue, \ 60 83 self.ErrAvalue, self.ErrBvalue, self.Chivalue = self.transform() 61 84 62 # Dialog interface 85 # Now set up the dialog interface 86 self.layout() 87 # Receives the type of model for the fitting 88 from LineModel import LineModel 89 self.model = LineModel() 90 # Display the fittings values 91 self.default_A = self.model.getParam('A') 92 self.default_B = self.model.getParam('B') 93 self.cstA = fittings.Parameter(self.model, 'A', self.default_A) 94 self.cstB = fittings.Parameter(self.model, 'B', self.default_B) 95 96 # Set default value of parameter in the dialog panel 97 if self.Avalue == None: 98 self.tcA.SetValue(format_number(self.default_A)) 99 else: 100 self.tcA.SetLabel(format_number(self.Avalue)) 101 if self.Bvalue == None: 102 self.tcB.SetValue(format_number(self.default_B)) 103 else: 104 self.tcB.SetLabel(format_number(self.Bvalue)) 105 if self.ErrAvalue == None: 106 self.tcErrA.SetLabel(format_number(0.0)) 107 else: 108 self.tcErrA.SetLabel(format_number(self.ErrAvalue)) 109 if self.ErrBvalue == None: 110 self.tcErrB.SetLabel(format_number(0.0)) 111 else: 112 self.tcErrB.SetLabel(format_number(self.ErrBvalue)) 113 if self.Chivalue == None: 114 self.tcChi.SetLabel(format_number(0.0)) 115 else: 116 self.tcChi.SetLabel(format_number(self.Chivalue)) 117 if self.plottable.x != []: 118 # store the values of View in self.x,self.y,self.dx,self.dy 119 self.x, self.y, self.dx, \ 120 self.dy = self.plottable.returnValuesOfView() 121 try: 122 self.mini = self.floatForwardTransform(min(self.x)) 123 except: 124 self.mini = "Invalid" 125 try: 126 self.maxi = self.floatForwardTransform(max(self.x)) 127 except: 128 self.maxi = "Invalid" 129 130 self.initXmin.SetValue(format_number(min(self.plottable.x))) 131 self.initXmax.SetValue(format_number(max(self.plottable.x))) 132 self.mini = min(self.x) 133 self.maxi = max(self.x) 134 self.xminFit.SetValue(format_number(self.mini)) 135 self.xmaxFit.SetValue(format_number(self.maxi)) 136 137 def layout(self): 138 """ 139 Sets up the panel layout for the linear fit including all the 140 labels, text entry boxes, and buttons. 141 142 """ 143 144 # set up sizers first. 145 # vbox is the panel sizer and is a vertical sizer 146 # The first element of the panel is sizer which is a gridbagsizer 147 # and contains most of the text fields 148 # this is followed by a line separator added to vbox 149 # and finally the sizer_button (a horizontal sizer) adds the buttons 63 150 vbox = wx.BoxSizer(wx.VERTICAL) 64 151 sizer = wx.GridBagSizer(5, 5) 152 sizer_button = wx.BoxSizer(wx.HORIZONTAL) 153 154 #size of string boxes in pixels 65 155 _BOX_WIDTH = 100 66 67 self.tcA = wx.TextCtrl(self, -1, size=(_BOX_WIDTH, 20)) 156 _BOX_HEIGHT = 20 157 #now set up all the text fields 158 self.tcA = wx.TextCtrl(self, -1, size=(_BOX_WIDTH, _BOX_HEIGHT)) 68 159 self.tcA.SetToolTipString("Fit value for the slope parameter.") 69 self.tcErrA = wx.TextCtrl(self, -1, size=(_BOX_WIDTH, 20))160 self.tcErrA = wx.TextCtrl(self, -1, size=(_BOX_WIDTH, _BOX_HEIGHT)) 70 161 self.tcErrA.SetToolTipString("Error on the slope parameter.") 71 self.tcB = wx.TextCtrl(self, -1, size=(_BOX_WIDTH, 20))162 self.tcB = wx.TextCtrl(self, -1, size=(_BOX_WIDTH, _BOX_HEIGHT)) 72 163 self.tcA.SetToolTipString("Fit value for the constant parameter.") 73 self.tcErrB = wx.TextCtrl(self, -1, size=(_BOX_WIDTH, 20))164 self.tcErrB = wx.TextCtrl(self, -1, size=(_BOX_WIDTH, _BOX_HEIGHT)) 74 165 self.tcErrB.SetToolTipString("Error on the constant parameter.") 75 self.tcChi = wx.TextCtrl(self, -1, size=(_BOX_WIDTH, 20))166 self.tcChi = wx.TextCtrl(self, -1, size=(_BOX_WIDTH, _BOX_HEIGHT)) 76 167 self.tcChi.SetToolTipString("Chi^2 over degrees of freedom.") 77 self.xminFit = wx.TextCtrl(self, -1, size=(_BOX_WIDTH, 20))168 self.xminFit = wx.TextCtrl(self, -1, size=(_BOX_WIDTH, _BOX_HEIGHT)) 78 169 msg = "Enter the minimum value on " 79 170 msg += "the x-axis to be included in the fit." 80 171 self.xminFit.SetToolTipString(msg) 81 self.xmaxFit = wx.TextCtrl(self, -1, size=(_BOX_WIDTH, 20))172 self.xmaxFit = wx.TextCtrl(self, -1, size=(_BOX_WIDTH, _BOX_HEIGHT)) 82 173 msg = "Enter the maximum value on " 83 174 msg += " the x-axis to be included in the fit." 84 175 self.xmaxFit.SetToolTipString(msg) 85 self.initXmin = wx.TextCtrl(self, -1, size=(_BOX_WIDTH, 20))176 self.initXmin = wx.TextCtrl(self, -1, size=(_BOX_WIDTH, _BOX_HEIGHT)) 86 177 msg = "Minimum value on the x-axis for the plotted data." 87 178 self.initXmin.SetToolTipString(msg) 88 self.initXmax = wx.TextCtrl(self, -1, size=(_BOX_WIDTH, 20))179 self.initXmax = wx.TextCtrl(self, -1, size=(_BOX_WIDTH, _BOX_HEIGHT)) 89 180 msg = "Maximum value on the x-axis for the plotted data." 90 181 self.initXmax.SetToolTipString(msg) … … 98 189 self.initXmax.SetBackgroundColour(_BACKGROUND_COLOR) 99 190 100 # Buttons on the bottom 191 #set some flags for specific types of fits like Guinier (Rg) and 192 #Porod (bg) -- this will determine WHAT boxes show up in the 193 #sizer layout and depends on the active axis transform 101 194 self.bg_on = False 102 self.static_line_1 = wx.StaticLine(self, -1)103 self.btFit = wx.Button(self, -1, 'Fit')104 self.btFit.Bind(wx.EVT_BUTTON, self._onFit)105 self.btFit.SetToolTipString("Perform fit.")106 self.btClose = wx.Button(self, wx.ID_CANCEL, 'Close')107 self.btClose.Bind(wx.EVT_BUTTON, self._on_close)108 195 if RG_ON: 109 196 if (self.yLabel == "ln(y)" or self.yLabel == "ln(y*x)") and \ … … 112 199 if (self.xLabel == "x^(4)") and (self.yLabel == "y*x^(4)"): 113 200 self.bg_on = True 114 # Intro 115 explanation = "Perform fit for y(x) = ax + b" 201 202 # Finally set up static text strings 203 warning = "WARNING! Resolution is NOT accounted for. \n" 204 warning += "Thus slit smeared data will give very wrong answers!" 205 self.textwarn = wx.StaticText(self, -1, warning) 206 self.textwarn.SetForegroundColour(wx.RED) 207 explanation = "Perform fit for y(x) = ax + b \n" 116 208 if self.bg_on: 117 209 param_a = 'Background (= Parameter a)' 118 210 else: 119 211 param_a = 'Parameter a' 120 vbox.Add(sizer) 212 213 214 #Now set this all up in the GridBagSizer sizer 121 215 ix = 0 122 iy = 1 216 iy = 0 217 sizer.Add(self.textwarn, (iy, ix), 218 (2, 3), wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15) 219 iy += 2 123 220 sizer.Add(wx.StaticText(self, -1, explanation), (iy, ix), 124 221 (1, 1), wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15) 125 iy += 2222 iy += 1 126 223 sizer.Add(wx.StaticText(self, -1, param_a), (iy, ix), 127 224 (1, 1), wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15) … … 281 378 wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 0) 282 379 380 #Now add some space before the separation line 283 381 iy += 1 284 ix = 1 285 286 vbox.Add(self.static_line_1, 0, wx.EXPAND, 0) 287 sizer_button = wx.BoxSizer(wx.HORIZONTAL) 382 ix = 0 383 sizer.Add((20,20), (iy, ix), (1, 1), 384 wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 0) 385 386 # Buttons on the bottom 387 self.btFit = wx.Button(self, -1, 'Fit') 388 self.btFit.Bind(wx.EVT_BUTTON, self._onFit) 389 self.btFit.SetToolTipString("Perform fit.") 390 self.btClose = wx.Button(self, wx.ID_CANCEL, 'Close') 391 self.btClose.Bind(wx.EVT_BUTTON, self._on_close) 288 392 sizer_button.Add((20, 20), 1, wx.EXPAND | wx.ADJUST_MINSIZE, 0) 289 sizer_button.Add(self.btFit, 0, wx.LEFT | wx.RIGHT | wx.ADJUST_MINSIZE, 10) 393 sizer_button.Add(self.btFit, 0, 394 wx.LEFT | wx.RIGHT | wx.ADJUST_MINSIZE, 10) 290 395 sizer_button.Add(self.btClose, 0, 291 396 wx.LEFT | wx.RIGHT | wx.ADJUST_MINSIZE, 10) 397 398 vbox.Add(sizer) 399 self.static_line_1 = wx.StaticLine(self, -1) 400 vbox.Add(self.static_line_1, 0, wx.EXPAND, 0) 292 401 vbox.Add(sizer_button, 0, wx.EXPAND | wx.BOTTOM | wx.TOP, 10) 293 402 294 sizer.Add(self.btFit, (iy, ix), (1, 1), wx.LEFT | wx.ADJUST_MINSIZE, 0)295 403 # panel.SetSizer(sizer) 296 404 self.SetSizer(vbox) 297 405 self.Centre() 298 # Receives the type of model for the fitting299 from LineModel import LineModel300 self.model = LineModel()301 # Display the fittings values302 self.default_A = self.model.getParam('A')303 self.default_B = self.model.getParam('B')304 self.cstA = fittings.Parameter(self.model, 'A', self.default_A)305 self.cstB = fittings.Parameter(self.model, 'B', self.default_B)306 307 # Set default value of parameter in fit dialog308 if self.Avalue == None:309 self.tcA.SetValue(format_number(self.default_A))310 else:311 self.tcA.SetLabel(format_number(self.Avalue))312 if self.Bvalue == None:313 self.tcB.SetValue(format_number(self.default_B))314 else:315 self.tcB.SetLabel(format_number(self.Bvalue))316 if self.ErrAvalue == None:317 self.tcErrA.SetLabel(format_number(0.0))318 else:319 self.tcErrA.SetLabel(format_number(self.ErrAvalue))320 if self.ErrBvalue == None:321 self.tcErrB.SetLabel(format_number(0.0))322 else:323 self.tcErrB.SetLabel(format_number(self.ErrBvalue))324 if self.Chivalue == None:325 self.tcChi.SetLabel(format_number(0.0))326 else:327 self.tcChi.SetLabel(format_number(self.Chivalue))328 if self.plottable.x != []:329 # store the values of View in self.x,self.y,self.dx,self.dy330 self.x, self.y, self.dx, \331 self.dy = self.plottable.returnValuesOfView()332 try:333 self.mini = self.floatForwardTransform(min(self.x))334 except:335 self.mini = "Invalid"336 try:337 self.maxi = self.floatForwardTransform(max(self.x))338 except:339 self.maxi = "Invalid"340 341 self.initXmin.SetValue(format_number(min(self.plottable.x)))342 self.initXmax.SetValue(format_number(max(self.plottable.x)))343 self.mini = min(self.x)344 self.maxi = max(self.x)345 self.xminFit.SetValue(format_number(self.mini))346 self.xmaxFit.SetValue(format_number(self.maxi))347 406 348 407 def register_close(self, owner): … … 371 430 the button Fit.Computes chisqr , 372 431 A and B parameters of the best linear fit y=Ax +B 373 Push a plottable to 432 Push a plottable to the caller 374 433 """ 375 434 tempx = [] … … 389 448 xmin = xminView 390 449 xmax = xmaxView 450 # Set the qmin and qmax in the panel that matches the 451 # transformed min and max 452 self.initXmin.SetValue(format_number(self.floatInvTransform(xmin))) 453 self.initXmax.SetValue(format_number(self.floatInvTransform(xmax))) 391 454 # Store the transformed values of view x, y,dy 392 455 # in variables before the fit … … 485 548 tempy.append(y_model) 486 549 # Set the fit parameter display when FitDialog is opened again 487 self.Avalue = cst B488 self.Bvalue = cst A550 self.Avalue = cstA 551 self.Bvalue = cstB 489 552 self.ErrAvalue = errA 490 553 self.ErrBvalue = errB … … 494 557 495 558 # Display the fitting value on the Fit Dialog 496 self._onsetValues(cst B, cstA, errA, errB, chisqr)559 self._onsetValues(cstA, cstB, errA, errB, chisqr) 497 560 498 561 def _onsetValues(self, cstA, cstB, errA, errB, Chi): … … 501 564 """ 502 565 rg = None 566 _diam = None 503 567 self.tcA.SetValue(format_number(cstA)) 504 568 self.tcB.SetValue(format_number(cstB)) … … 524 588 self.I0err_tctr.SetValue(format_number(val)) 525 589 if self.Diameter_tctr.IsShown(): 526 rg = 4 * numpy.sqrt(-float(cstA)) 527 value = format_number(rg) 590 rg = numpy.sqrt(-2 * float(cstA)) 591 _diam = 4 * numpy.sqrt(-float(cstA)) 592 value = format_number(_diam) 528 593 self.Diameter_tctr.SetValue(value) 529 594 if self.Diametererr_tctr.IsShown(): 530 595 if rg != None and rg != 0: 531 value = format_number(8 * float( cstA) / rg)596 value = format_number(8 * float(errA) / _diam) 532 597 else: 533 598 value = '' … … 610 675 def floatInvTransform(self, x): 611 676 """ 612 transform a float.It is use to determine the x.View min and x.View 613 max for values not in x 677 transform a float.It is used to determine the x.View min and x.View 678 max for values not in x. Also used to properly calculate RgQmin, 679 RgQmax and to update qmin and qmax in the linear range boxes on the 680 panel. 614 681 615 682 """ … … 617 684 # functionality work without rewritting the whole code 618 685 # with good design (which really should be done...). 619 if self.xLabel == "x^(2)": 686 if self.xLabel == "x": 687 return x 688 elif self.xLabel == "x^(2)": 620 689 return math.sqrt(x) 690 elif self.xLabel == "x^(4)": 691 return math.sqrt(math.sqrt(x)) 621 692 elif self.xLabel == "log10(x)": 622 693 return math.pow(10, x) 623 694 elif self.xLabel == "ln(x)": 624 695 return math.exp(x) 696 elif self.xLabel == "log10(x^(4))": 697 return math.sqrt(math.sqrt(math.pow(10, x))) 625 698 return x 626 699 -
src/sas/sasgui/plottools/fittings.py
rd7bb526 rdd5bf63 1 1 """ 2 This module is used to fit a set of x,y data to a model passed to it. It is 3 used to calculate the slope and intercepts for the linearized fits. Two things 4 should be noted: 5 6 First, this fitting module uses the NLLSQ module of SciPy rather than a linear 7 fit. This along with a few other modules could probably be removed if we 8 move to a linear regression approach. 9 10 Second, this infrastructure does not allow for resolution smearing of the 11 the models. Hence the results are not that accurate even for pinhole 12 collimation of SANS but may be good for SAXS. It is completely wrong for 13 slit smeared data. 14 2 15 """ 3 16 from scipy import optimize … … 6 19 class Parameter(object): 7 20 """ 8 Class to handle model parameters 21 Class to handle model parameters - sets the parameters and their 22 initial value from the model based to it. 9 23 """ 10 24 def __init__(self, model, name, value=None):
Note: See TracChangeset
for help on using the changeset viewer.