source: sasview/prview/perspectives/pr/pr.py @ 7cb0353

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalccostrafo411magnetic_scattrelease-4.1.1release-4.1.2release-4.2.2release_4.0.1ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since 7cb0353 was 7cb0353, checked in by Mathieu Doucet <doucetm@…>, 16 years ago

Delete test menu item

  • Property mode set to 100644
File size: 19.4 KB
Line 
1#TODO: Use simview to generate P(r) and I(q) pairs in sansview.
2# Make sure the option of saving each curve is available
3# Use the I(q) curve as input and compare the output to P(r)
4
5import os
6import wx
7from sans.guitools.plottables import Data1D, Theory1D
8from sans.guicomm.events import NewPlotEvent, StatusEvent   
9import math, numpy
10from sans.pr.invertor import Invertor
11
12class Plugin:
13   
14    def __init__(self):
15        ## Plug-in name
16        self.sub_menu = "Pr inversion"
17       
18        ## Reference to the parent window
19        self.parent = None
20       
21        ## Simulation window manager
22        self.simview = None
23       
24        ## List of panels for the simulation perspective (names)
25        self.perspective = []
26       
27        ## State data
28        self.alpha      = 0.0001
29        self.nfunc      = 10
30        self.max_length = 140.0
31        self.q_min      = None
32        self.q_max      = None
33        ## Remember last plottable processed
34        self.last_data  = "sphere_60_q0_2.txt"
35        ## Time elapsed for last computation [sec]
36        # Start with a good default
37        self.elapsed = 0.022
38       
39        ## Current invertor
40        self.invertor    = None
41        ## Calculation thread
42        self.calc_thread = None
43        ## Estimation thread
44        self.estimation_thread = None
45        ## Result panel
46        self.control_panel = None
47        ## Currently views plottable
48        self.current_plottable = None
49
50    def populate_menu(self, id, owner):
51        """
52            Create a menu for the plug-in
53        """
54        return []
55        import wx
56        shapes = wx.Menu()
57
58        id = wx.NewId()
59        shapes.Append(id, '&Sphere test')
60        wx.EVT_MENU(owner, id, self._fit_pr)
61       
62        return [(id, shapes, "P(r)")]
63   
64    def help(self, evt):
65        """
66            Show a general help dialog.
67            TODO: replace the text with a nice image
68        """
69        from inversion_panel import HelpDialog
70        dialog = HelpDialog(None, -1)
71        if dialog.ShowModal() == wx.ID_OK:
72            dialog.Destroy()
73        else:
74            dialog.Destroy()
75   
76    def _fit_pr(self, evt):
77        from sans.pr.invertor import Invertor
78        import numpy
79        import pylab
80        import math
81        from sans.guicomm.events import NewPlotEvent           
82        from sans.guitools.plottables import Data1D, Theory1D
83       
84        # Generate P(r) for sphere
85        radius = 60.0
86        d_max  = 2*radius
87       
88       
89        r = pylab.arange(0.01, d_max, d_max/51.0)
90        M = len(r)
91        y = numpy.zeros(M)
92        pr_err = numpy.zeros(M)
93       
94        sum = 0.0
95        for j in range(M):
96            value = self.pr_theory(r[j], radius)
97            sum += value
98            y[j] = value
99            pr_err[j] = math.sqrt(y[j])
100
101           
102        y = y/sum*d_max/len(r)
103
104
105
106        # Perform fit
107        pr = Invertor()
108        pr.d_max = d_max
109        pr.alpha = 0
110        pr.x = r
111        pr.y = y
112        pr.err = pr_err
113        out, cov = pr.pr_fit()
114        for i in range(len(out)):
115            print "%g +- %g" % (out[i], math.sqrt(cov[i][i]))
116
117
118        # Show input P(r)
119        new_plot = Data1D(pr.x, pr.y, pr.err)
120        new_plot.name = "P_{obs}(r)"
121        new_plot.xaxis("\\rm{r}", 'A')
122        new_plot.yaxis("\\rm{P(r)} ","cm^{-3}")
123        wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot, title="Pr"))
124
125        # Show P(r) fit
126        self.show_pr(out, pr)
127       
128        # Show I(q) fit
129        q = pylab.arange(0.001, 0.1, 0.01/51.0)
130        self.show_iq(out, pr, q)
131       
132       
133    def show_shpere(self, x, radius=70.0, x_range=70.0):
134        import numpy
135        import pylab
136        import math
137        from sans.guicomm.events import NewPlotEvent           
138        from sans.guitools.plottables import Data1D, Theory1D
139        # Show P(r)
140        y_true = numpy.zeros(len(x))
141
142        sum_true = 0.0
143        for i in range(len(x)):
144            y_true[i] = self.pr_theory(x[i], radius)           
145            sum_true += y_true[i]
146           
147        y_true = y_true/sum_true*x_range/len(x)
148       
149        # Show the theory P(r)
150        new_plot = Theory1D(x, y_true)
151        new_plot.name = "P_{true}(r)"
152        new_plot.xaxis("\\rm{r}", 'A')
153        new_plot.yaxis("\\rm{P(r)} ","cm^{-3}")
154       
155       
156        #Put this call in plottables/guitools   
157        wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot, title="Sphere P(r)"))
158       
159    def show_iq(self, out, pr, q=None):
160        import numpy
161        import pylab
162        import math
163        from sans.guicomm.events import NewPlotEvent           
164        from sans.guitools.plottables import Data1D, Theory1D
165
166        qtemp = pr.x
167        if not q==None:
168            qtemp = q
169
170        # Make a plot
171        maxq = -1
172        for q_i in qtemp:
173            if q_i>maxq:
174                maxq=q_i
175               
176        minq = 0.001
177       
178        # Check for user min/max
179        if not pr.q_min==None:
180            minq = pr.q_min
181        if not pr.q_max==None:
182            maxq = pr.q_max
183               
184        x = pylab.arange(minq, maxq, maxq/301.0)
185        y = numpy.zeros(len(x))
186        err = numpy.zeros(len(x))
187        for i in range(len(x)):
188            value = pr.iq(out, x[i])
189            y[i] = value
190            try:
191                err[i] = math.sqrt(math.fabs(value))
192            except:
193                err[i] = 1.0
194                print "Error getting error", value, x[i]
195               
196        new_plot = Theory1D(x, y)
197        new_plot.name = "I_{fit}(q)"
198        new_plot.xaxis("\\rm{Q}", 'A^{-1}')
199        new_plot.yaxis("\\rm{Intensity} ","cm^{-1}")
200        #new_plot.group_id = "test group"
201        wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot, title="Iq"))
202       
203       
204       
205       
206    def show_pr(self, out, pr, cov=None):
207        import numpy
208        import pylab
209        import math
210        from sans.guicomm.events import NewPlotEvent           
211        from sans.guitools.plottables import Data1D, Theory1D
212       
213        # Show P(r)
214        x = pylab.arange(0.0, pr.d_max, pr.d_max/51.0)
215   
216        y = numpy.zeros(len(x))
217        dy = numpy.zeros(len(x))
218        y_true = numpy.zeros(len(x))
219
220        sum = 0.0
221        cov2 = numpy.ascontiguousarray(cov)
222       
223        for i in range(len(x)):
224            if cov2==None:
225                value = pr.pr(out, x[i])
226            else:
227                (value, dy[i]) = pr.pr_err(out, cov2, x[i])
228            sum += value
229            y[i] = value
230           
231        y = y/sum*pr.d_max/len(x)
232        dy = dy/sum*pr.d_max/len(x)
233       
234        if cov2==None:
235            new_plot = Theory1D(x, y)
236        else:
237            new_plot = Data1D(x, y, dy=dy)
238        new_plot.name = "P_{fit}(r)"
239        new_plot.xaxis("\\rm{r}", 'A')
240        new_plot.yaxis("\\rm{P(r)} ","cm^{-3}")
241           
242        wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot, title="P(r) fit"))
243       
244        return x, pr.d_max
245       
246       
247    def choose_file(self):
248        """
249       
250        """
251        #TODO: this should be in a common module
252        return self.parent.choose_file()
253               
254    def load(self, path = "sphere_test_data.txt"):
255        import numpy, math, sys
256        # Read the data from the data file
257        data_x   = numpy.zeros(0)
258        data_y   = numpy.zeros(0)
259        data_err = numpy.zeros(0)
260        if not path == None:
261            input_f = open(path,'r')
262            buff    = input_f.read()
263            lines   = buff.split('\n')
264            for line in lines:
265                try:
266                    toks = line.split()
267                    x = float(toks[0])
268                    y = float(toks[1])
269                    data_x = numpy.append(data_x, x)
270                    data_y = numpy.append(data_y, y)
271                    try:
272                        scale = 0.05/math.sqrt(data_x[0])
273                    except:
274                        scale = 1.0
275                    #data_err = numpy.append(data_err, 10.0*math.sqrt(y)+1000.0)
276                    data_err = numpy.append(data_err, scale*math.sqrt(y))
277                except:
278                    print "Error reading line: ", line
279                    print sys.exc_value
280                   
281        print "Lines read:", len(data_x)
282        return data_x, data_y, data_err
283       
284    def pr_theory(self, r, R):
285        """
286           
287        """
288        if r<=2*R:
289            return 12.0* ((0.5*r/R)**2) * ((1.0-0.5*r/R)**2) * ( 2.0 + 0.5*r/R )
290        else:
291            return 0.0
292
293    def get_context_menu(self, plot_id=None):
294        """
295            Get the context menu items available for P(r)
296            @param plot_id: Unique ID of a plot, so that we can recognize those
297                            that we created
298            @return: a list of menu items with call-back function
299        """
300        return [["Compute P(r)", "Compute P(r) from distribution", self._on_context_inversion]]
301   
302
303    def start_thread(self):
304        from pr_thread import CalcPr
305        from copy import deepcopy
306       
307        # If a thread is already started, stop it
308        if self.calc_thread != None and self.calc_thread.isrunning():
309            self.calc_thread.stop()
310               
311        pr = self.pr.clone()
312        self.calc_thread = CalcPr(pr, self.nfunc, error_func=self._thread_error, completefn=self._completed, updatefn=None)
313        self.calc_thread.queue()
314        self.calc_thread.ready(2.5)
315   
316    def _thread_error(self, error):
317        wx.PostEvent(self.parent, StatusEvent(status=error))
318   
319    def _estimate_completed(self, alpha, message, elapsed):
320        """
321            Parameter estimation completed,
322            display the results to the user
323            @param alpha: estimated best alpha
324            @param elapsed: computation time
325        """
326        # Save useful info
327        self.elapsed = elapsed
328        self.control_panel.alpha_estimate = alpha
329        if not message==None:
330            wx.PostEvent(self.parent, StatusEvent(status=str(message)))
331   
332    def _completed(self, out, cov, pr, elapsed):
333        """
334            Method called with the results when the inversion
335            is done
336           
337            @param out: output coefficient for the base functions
338            @param cov: covariance matrix
339            @param pr: Invertor instance
340            @param elapsed: time spent computing
341        """
342        from copy import deepcopy
343        # Save useful info
344        self.elapsed = elapsed
345        message = "Computation completed in %g seconds [chi2=%g]" % (elapsed, pr.chi2)
346        wx.PostEvent(self.parent, StatusEvent(status=message))
347
348        cov = numpy.ascontiguousarray(cov)
349
350        # Show result on control panel
351        self.control_panel.chi2 = pr.chi2
352        self.control_panel.elapsed = elapsed
353        self.control_panel.oscillation = pr.oscillations(out)
354        #print "OSCILL", pr.oscillations(out)
355        print "PEAKS:", pr.get_peaks(out) 
356        self.control_panel.positive = pr.get_positive(out)
357        self.control_panel.pos_err  = pr.get_pos_err(out, cov)
358       
359        for i in range(len(out)):
360            try:
361                print "%d: %g +- %g" % (i, out[i], math.sqrt(math.fabs(cov[i][i])))
362            except: 
363                print "%d: %g +- ?" % (i, out[i])       
364       
365        # Make a plot of I(q) data
366        new_plot = Data1D(self.pr.x, self.pr.y, self.pr.err)
367        new_plot.name = "I_{obs}(q)"
368        new_plot.xaxis("\\rm{Q}", 'A^{-1}')
369        new_plot.yaxis("\\rm{Intensity} ","cm^{-1}")
370        #new_plot.group_id = "test group"
371        wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot, title="Iq"))
372               
373        # Show I(q) fit
374        self.show_iq(out, self.pr)
375       
376        # Show P(r) fit
377        x_values, x_range = self.show_pr(out, self.pr, cov) 
378       
379        # Popup result panel
380        #result_panel = InversionResults(self.parent, -1, style=wx.RAISED_BORDER)
381       
382    def setup_plot_inversion(self, alpha, nfunc, d_max, q_min=None, q_max=None):
383        self.alpha = alpha
384        self.nfunc = nfunc
385        self.max_length = d_max
386        self.q_min = q_min
387        self.q_max = q_max
388       
389        self._create_plot_pr()
390        self.perform_inversion()
391
392    def estimate_plot_inversion(self, alpha, nfunc, d_max, q_min=None, q_max=None):
393        self.alpha = alpha
394        self.nfunc = nfunc
395        self.max_length = d_max
396        self.q_min = q_min
397        self.q_max = q_max
398       
399        self._create_plot_pr()
400        self.perform_estimate()
401
402    def _create_plot_pr(self):
403        """
404            Create and prepare invertor instance from
405            a plottable data set.
406            @param path: path of the file to read in
407        """
408        # Get the data from the chosen data set and perform inversion
409        pr = Invertor()
410        pr.d_max = self.max_length
411        pr.alpha = self.alpha
412        pr.q_min = self.q_min
413        pr.q_max = self.q_max
414        pr.x = self.current_plottable.x
415        pr.y = self.current_plottable.y
416       
417        # Fill in errors if none were provided
418        if self.current_plottable.dy == None:
419            print "no error", self.current_plottable.name
420            y = numpy.zeros(len(pr.y))
421            for i in range(len(pr.y)):
422                y[i] = math.sqrt(pr.y[i])
423            pr.err = y
424        else:
425            pr.err = self.current_plottable.dy
426           
427        self.pr = pr
428
429         
430    def setup_file_inversion(self, alpha, nfunc, d_max, path, q_min=None, q_max=None):
431        self.alpha = alpha
432        self.nfunc = nfunc
433        self.max_length = d_max
434        self.q_min = q_min
435        self.q_max = q_max
436       
437        self._create_file_pr(path)
438       
439        self.perform_inversion()
440         
441    def estimate_file_inversion(self, alpha, nfunc, d_max, path, q_min=None, q_max=None):
442        self.alpha = alpha
443        self.nfunc = nfunc
444        self.max_length = d_max
445        self.q_min = q_min
446        self.q_max = q_max
447       
448        if self._create_file_pr(path):
449            self.perform_estimate()
450         
451    def _create_file_pr(self, path):
452        """
453            Create and prepare invertor instance from
454            a file data set.
455            @param path: path of the file to read in
456        """
457        # Load data
458        if os.path.isfile(path):
459            x, y, err = self.load(path)
460           
461            # Get the data from the chosen data set and perform inversion
462            pr = Invertor()
463            pr.d_max = self.max_length
464            pr.alpha = self.alpha
465            pr.q_min = self.q_min
466            pr.q_max = self.q_max
467            pr.x = x
468            pr.y = y
469            pr.err = err
470           
471            self.pr = pr
472            return True
473        return False
474       
475    def perform_estimate(self):
476        from pr_thread import EstimatePr
477        from copy import deepcopy
478       
479        wx.PostEvent(self.parent, StatusEvent(status=''))
480        # If a thread is already started, stop it
481        if self.estimation_thread != None and self.estimation_thread.isrunning():
482            self.estimation_thread.stop()
483               
484        pr = self.pr.clone()
485        self.estimation_thread = EstimatePr(pr, self.nfunc, error_func=self._thread_error, 
486                                            completefn = self._estimate_completed, 
487                                            updatefn   = None)
488        self.estimation_thread.queue()
489        self.estimation_thread.ready(2.5)
490       
491    def perform_inversion(self):
492       
493        # Time estimate
494        #estimated = self.elapsed*self.nfunc**2
495        message = "Computation time may take up to %g seconds" % self.elapsed
496        wx.PostEvent(self.parent, StatusEvent(status=message))
497       
498        # Start inversion thread
499        self.start_thread()
500        return
501       
502        out, cov = self.pr.lstsq(self.nfunc)
503       
504        # Save useful info
505        self.elapsed = self.pr.elapsed
506       
507        for i in range(len(out)):
508            try:
509                print "%d: %g +- %g" % (i, out[i], math.sqrt(math.fabs(cov[i][i])))
510            except: 
511                print "%d: %g +- ?" % (i, out[i])       
512       
513       
514       
515        # Make a plot of I(q) data
516        new_plot = Data1D(self.pr.x, self.pr.y, dy=self.pr.err)
517        new_plot.name = "I_{obs}(q)"
518        new_plot.xaxis("\\rm{Q}", 'A^{-1}')
519        new_plot.yaxis("\\rm{Intensity} ","cm^{-1}")
520        wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot, title="Iq"))
521               
522        # Show I(q) fit
523        self.show_iq(out, self.pr)
524       
525        # Show P(r) fit
526        x_values, x_range = self.show_pr(out, self.pr, cov=cov)
527       
528       
529         
530         
531    def _on_context_inversion(self, event):
532        panel = event.GetEventObject()
533
534        from inversion_panel import InversionDlg
535       
536        # If we have more than one displayed plot, make the user choose
537        if len(panel.plots)>1:
538            dialog = InversionDlg(None, -1, "P(r) Inversion", panel.plots, pars=False)
539            dialog.set_content(self.last_data, self.nfunc, self.alpha, self.max_length)
540            if dialog.ShowModal() == wx.ID_OK:
541                dataset = dialog.get_content()
542                dialog.Destroy()
543            else:
544                dialog.Destroy()
545                return
546        elif len(panel.plots)==1:
547            dataset = panel.plots.keys()[0]
548        else:
549            print "Error: No data is available"
550            return
551       
552        # Store a reference to the current plottable
553        self.current_plottable = panel.plots[dataset]
554        self.control_panel.plotname = dataset
555        self.control_panel.nfunc = self.nfunc
556        self.control_panel.d_max = self.max_length
557        self.control_panel.alpha = self.alpha
558        self.parent.set_perspective(self.perspective)
559           
560    def get_panels(self, parent):
561        """
562            Create and return a list of panel objects
563        """
564        from inversion_panel import InversionControl
565       
566        self.parent = parent
567        self.control_panel = InversionControl(self.parent, -1, style=wx.RAISED_BORDER)
568        self.control_panel.set_manager(self)
569        self.control_panel.nfunc = self.nfunc
570        self.control_panel.d_max = self.max_length
571        self.control_panel.alpha = self.alpha
572       
573        self.perspective = []
574        self.perspective.append(self.control_panel.window_name)
575        return [self.control_panel]
576   
577    def get_perspective(self):
578        """
579            Get the list of panel names for this perspective
580        """
581        return self.perspective
582   
583    def on_perspective(self, event):
584        """
585            Call back function for the perspective menu item.
586            We notify the parent window that the perspective
587            has changed.
588        """
589        self.parent.set_perspective(self.perspective)
590   
591    def post_init(self):
592        """
593            Post initialization call back to close the loose ends
594            [Somehow openGL needs this call]
595        """
596        self.parent.set_perspective(self.perspective)
597   
Note: See TracBrowser for help on using the repository browser.