source: sasview/prview/perspectives/pr/pr.py @ 511c6810

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 511c6810 was c8afcb7, checked in by Gervaise Alina <gervyh@…>, 14 years ago

fix prview

  • Property mode set to 100644
File size: 49.4 KB
RevLine 
[9ff861b]1
[7116b6e0]2################################################################################
3#This software was developed by the University of Tennessee as part of the
4#Distributed Data Analysis of Neutron Scattering Experiments (DANSE)
5#project funded by the US National Science Foundation.
6#
7#See the license text in license.txt
8#
9#copyright 2009, University of Tennessee
10################################################################################
[9ff861b]11
12
[f3d51f6]13# Make sure the option of saving each curve is available
14# Use the I(q) curve as input and compare the output to P(r)
15
16import os
[4a5de6f]17import sys
[f3d51f6]18import wx
[aa4b8379]19import logging
[302510b]20import time
[75df58b]21import copy
22import math
23import numpy
24import pylab
25from sans.guiframe.dataFitting import Data1D
26from sans.guiframe.events import NewPlotEvent
[a07e72f]27from sans.guiframe.events import StatusEvent
28from sans.guiframe.gui_style import GUIFRAME_ID   
[f3d51f6]29from sans.pr.invertor import Invertor
[aab8ad7]30from DataLoader.loader import Loader
[a07e72f]31
[af91b68]32from pr_widgets import load_error
[75df58b]33from sans.guiframe.plugin_base import PluginBase
[f3d51f6]34
[0d88a09]35
[f1181977]36PR_FIT_LABEL       = r"$P_{fit}(r)$"
37PR_LOADED_LABEL    = r"$P_{loaded}(r)$"
38IQ_DATA_LABEL      = r"$I_{obs}(q)$"
39IQ_FIT_LABEL       = r"$I_{fit}(q)$"
40IQ_SMEARED_LABEL   = r"$I_{smeared}(q)$"
[aa4b8379]41
42
[3e41f43]43class Plugin(PluginBase):
[7116b6e0]44    """
45    """
[b659551]46    DEFAULT_ALPHA = 0.0001
47    DEFAULT_NFUNC = 10
48    DEFAULT_DMAX  = 140.0
49   
[d2ee6f6]50    def __init__(self, standalone=True):
[3e41f43]51        PluginBase.__init__(self, name="Pr inversion", standalone=standalone)
[f3d51f6]52        ## Simulation window manager
53        self.simview = None
[3e41f43]54       
[f3d51f6]55        ## State data
[b659551]56        self.alpha      = self.DEFAULT_ALPHA
57        self.nfunc      = self.DEFAULT_NFUNC
58        self.max_length = self.DEFAULT_DMAX
[634f1cf]59        self.q_min      = None
60        self.q_max      = None
[a4bd2ac]61        self.has_bck    = False
[2a92852]62        self.slit_height = 0
63        self.slit_width  = 0
[f3d51f6]64        ## Remember last plottable processed
65        self.last_data  = "sphere_60_q0_2.txt"
[0bae207]66        self._current_file_data = None
[f3d51f6]67        ## Time elapsed for last computation [sec]
68        # Start with a good default
69        self.elapsed = 0.022
[aa4b8379]70        self.iq_data_shown = False
[f3d51f6]71       
72        ## Current invertor
73        self.invertor    = None
[3fd1ebc]74        self.pr          = None
[db4ed82]75        self.data_id = IQ_DATA_LABEL
[feb21d8]76        # Copy of the last result in case we need to display it.
77        self._last_pr    = None
78        self._last_out   = None
79        self._last_cov   = None
[f3d51f6]80        ## Calculation thread
81        self.calc_thread = None
82        ## Estimation thread
83        self.estimation_thread = None
84        ## Result panel
85        self.control_panel = None
86        ## Currently views plottable
87        self.current_plottable = None
[b659551]88        ## Number of P(r) points to display on the output plot
89        self._pr_npts = 51
[aa4b8379]90        ## Flag to let the plug-in know that it is running standalone
[d2ee6f6]91        self.standalone = standalone
[feb21d8]92        self._normalize_output = False
93        self._scale_output_unity = False
[aa4b8379]94       
[f1181977]95        ## List of added P(r) plots
96        self._added_plots = {}
97        self._default_Iq  = {}
[91d910d]98        self.list_plot_id = []
[f1181977]99       
[6f1f129]100        # Associate the inversion state reader with .prv files
101        from inversion_state import Reader
102         
[0d88a09]103        # Create a CanSAS/Pr reader
104        self.state_reader = Reader(self.set_state)
[6d3d5ff]105        self._extensions = '.prv'
[6f1f129]106        l = Loader()
[410aad8]107        l.associate_file_reader('.prv', self.state_reader)
[6d3d5ff]108        l.associate_file_reader(".svs", self.state_reader)
[6f1f129]109               
[aa4b8379]110        # Log startup
111        logging.info("Pr(r) plug-in started")
112       
[91d910d]113    def delete_data(self, data_id):
114        """
115        delete the data association with prview
116        """
117        if self.calc_thread is not None and self.calc_thread.isrunning():
118            msg = "Data in use in Prview\n"
119        if self.estimation_thread is not None and \
120            self.estimation_thread.isrunning():
121             msg = "Data in use in Prview\n"
122        self.control_panel.clear_panel()
123       
[6d3d5ff]124    def get_data(self):
125        """
126        """
127        return self.current_plottable
128   
[75fbd17]129    def set_state(self, state=None, datainfo=None):
[6f1f129]130        """
[7116b6e0]131        Call-back method for the inversion state reader.
132        This method is called when a .prv file is loaded.
133       
134        :param state: InversionState object
135        :param datainfo: Data1D object [optional]
136       
[6f1f129]137        """
[410aad8]138        try:
[75fbd17]139            if datainfo.__class__.__name__ == 'list':
[c03b780]140                if len(datainfo) >= 1:
[75fbd17]141                    data = datainfo[0]
[c03b780]142                else:
143                    data = None
144            else:
145                data = datainfo
[75fbd17]146            if data is None:
[0d88a09]147                raise RuntimeError, "Pr.set_state: datainfo parameter cannot be None in standalone mode"
[229da98]148           
[0d88a09]149            # Ensuring that plots are coordinated correctly
[75fbd17]150            t = time.localtime(data.meta_data['prstate'].timestamp)
[0d88a09]151            time_str = time.strftime("%b %d %H:%M", t)
152           
153            # Check that no time stamp is already appended
[75fbd17]154            max_char = data.meta_data['prstate'].file.find("[")
[0d88a09]155            if max_char < 0:
[75fbd17]156                max_char = len(data.meta_data['prstate'].file)
[0d88a09]157           
[229da98]158            datainfo.meta_data['prstate'].file = data.meta_data['prstate'].file[0:max_char] +' [' + time_str + ']'
[75fbd17]159            data.filename = data.meta_data['prstate'].file
[053c769]160            # TODO:
161            #remove this call when state save all information about the gui data
162            # such as ID , Group_ID, etc...
163            #make self.current_plottable = datainfo directly
[229da98]164            self.current_plottable = self.parent.create_gui_data(data,None)
165            self.current_plottable.group_id = data.meta_data['prstate'].file
[75fbd17]166           
167            # Make sure the user sees the P(r) panel after loading
168            #self.parent.set_perspective(self.perspective) 
169            self.on_perspective(event=None)   
[410aad8]170            # Load the P(r) results
[229da98]171            #state = self.state_reader.get_state()
[c8afcb7]172            data_dict = {self.current_plottable.id:self.current_plottable}
173            self.parent.add_data(data_list=data_dict)
[75fbd17]174            wx.PostEvent(self.parent, NewPlotEvent(plot=self.current_plottable,
175                                        title=self.current_plottable.title))
[410aad8]176            self.control_panel.set_state(state)
177        except:
[a07e72f]178            logging.error("prview.set_state: %s" % sys.exc_value)
[f3d51f6]179
[8994b6b]180 
[119a11d]181    def help(self, evt):
182        """
[7116b6e0]183        Show a general help dialog.
184       
185        :TODO: replace the text with a nice image
186       
[119a11d]187        """
188        from inversion_panel import HelpDialog
189        dialog = HelpDialog(None, -1)
190        if dialog.ShowModal() == wx.ID_OK:
191            dialog.Destroy()
192        else:
193            dialog.Destroy()
194   
[f3d51f6]195    def _fit_pr(self, evt):
[7116b6e0]196        """
197        """
[f3d51f6]198        from sans.pr.invertor import Invertor
199        # Generate P(r) for sphere
200        radius = 60.0
201        d_max  = 2*radius
202       
203        r = pylab.arange(0.01, d_max, d_max/51.0)
204        M = len(r)
205        y = numpy.zeros(M)
206        pr_err = numpy.zeros(M)
207       
208        sum = 0.0
209        for j in range(M):
210            value = self.pr_theory(r[j], radius)
211            sum += value
212            y[j] = value
213            pr_err[j] = math.sqrt(y[j])
214
215        y = y/sum*d_max/len(r)
216
217        # Perform fit
218        pr = Invertor()
219        pr.d_max = d_max
220        pr.alpha = 0
221        pr.x = r
222        pr.y = y
223        pr.err = pr_err
224        out, cov = pr.pr_fit()
225        for i in range(len(out)):
226            print "%g +- %g" % (out[i], math.sqrt(cov[i][i]))
[a07e72f]227       
[f3d51f6]228        # Show input P(r)
[a07e72f]229        title = "Pr"
230        new_plot = Data1D(pr.x, pr.y, dy=pr.err)
[f3d51f6]231        new_plot.name = "P_{obs}(r)"
232        new_plot.xaxis("\\rm{r}", 'A')
233        new_plot.yaxis("\\rm{P(r)} ","cm^{-3}")
[e88ebfd]234        new_plot.group_id = "P_{obs}(r)"
[a07e72f]235        new_plot.id = "P_{obs}(r)"
236        new_plot.title = title
[db4ed82]237        self.parent.update_theory(data_id=self.data_id,  theory=new_plot)
[a07e72f]238        wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot, title=title))
[f3d51f6]239
240        # Show P(r) fit
241        self.show_pr(out, pr)
242       
243        # Show I(q) fit
244        q = pylab.arange(0.001, 0.1, 0.01/51.0)
245        self.show_iq(out, pr, q)
246       
247    def show_shpere(self, x, radius=70.0, x_range=70.0):
[7116b6e0]248        """
249        """
[f3d51f6]250        # Show P(r)
251        y_true = numpy.zeros(len(x))
252
253        sum_true = 0.0
254        for i in range(len(x)):
255            y_true[i] = self.pr_theory(x[i], radius)           
256            sum_true += y_true[i]
257           
258        y_true = y_true/sum_true*x_range/len(x)
259       
260        # Show the theory P(r)
[a07e72f]261        new_plot = Data1D(x, y_true)
262        new_plot.symbol = GUIFRAME_ID.CURVE_SYMBOL_NUM
[f3d51f6]263        new_plot.name = "P_{true}(r)"
264        new_plot.xaxis("\\rm{r}", 'A')
265        new_plot.yaxis("\\rm{P(r)} ","cm^{-3}")
[a3149c5]266        new_plot.id = "P_{true}(r)"
[e88ebfd]267        new_plot.group_id = "P_{true}(r)"
[db4ed82]268        self.parent.update_theory(data_id=self.data_id, theory=new_plot)
[f3d51f6]269        #Put this call in plottables/guitools   
[db4ed82]270        wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot,
271                                                title="Sphere P(r)"))
[75df58b]272       
[f3d51f6]273       
[b659551]274    def get_npts(self):
275        """
[7116b6e0]276        Returns the number of points in the I(q) data
[b659551]277        """
278        try:
279            return len(self.pr.x)
280        except:
281            return 0
282       
[f3d51f6]283    def show_iq(self, out, pr, q=None):
[7116b6e0]284        """
[75df58b]285        """ 
[f3d51f6]286        qtemp = pr.x
287        if not q==None:
288            qtemp = q
289
290        # Make a plot
291        maxq = -1
292        for q_i in qtemp:
293            if q_i>maxq:
294                maxq=q_i
295               
[634f1cf]296        minq = 0.001
297       
298        # Check for user min/max
299        if not pr.q_min==None:
300            minq = pr.q_min
301        if not pr.q_max==None:
302            maxq = pr.q_max
303               
304        x = pylab.arange(minq, maxq, maxq/301.0)
[f3d51f6]305        y = numpy.zeros(len(x))
306        err = numpy.zeros(len(x))
307        for i in range(len(x)):
308            value = pr.iq(out, x[i])
309            y[i] = value
310            try:
311                err[i] = math.sqrt(math.fabs(value))
312            except:
313                err[i] = 1.0
314                print "Error getting error", value, x[i]
315               
[a07e72f]316        new_plot = Data1D(x, y)
317        new_plot.symbol = GUIFRAME_ID.CURVE_SYMBOL_NUM
[2a92852]318        new_plot.name = IQ_FIT_LABEL
[f3d51f6]319        new_plot.xaxis("\\rm{Q}", 'A^{-1}')
320        new_plot.yaxis("\\rm{Intensity} ","cm^{-1}")
[d2ee6f6]321        title = "I(q)"
[a07e72f]322        new_plot.title = title
323       
[d2ee6f6]324        # If we have a group ID, use it
325        if pr.info.has_key("plot_group_id"):
[e88ebfd]326            new_plot.group_id = pr.info["plot_group_id"]
[a07e72f]327        new_plot.id = IQ_FIT_LABEL
[db4ed82]328        self.parent.update_theory(data_id=self.data_id, theory=new_plot)
[d2ee6f6]329        wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot, title=title))
[f3d51f6]330       
[2a92852]331        # If we have used slit smearing, plot the smeared I(q) too
332        if pr.slit_width>0 or pr.slit_height>0:
333            x = pylab.arange(minq, maxq, maxq/301.0)
334            y = numpy.zeros(len(x))
335            err = numpy.zeros(len(x))
336            for i in range(len(x)):
337                value = pr.iq_smeared(out, x[i])
338                y[i] = value
339                try:
340                    err[i] = math.sqrt(math.fabs(value))
341                except:
342                    err[i] = 1.0
343                    print "Error getting error", value, x[i]
344                   
[a07e72f]345            new_plot = Data1D(x, y)
346            new_plot.symbol = GUIFRAME_ID.CURVE_SYMBOL_NUM
[2a92852]347            new_plot.name = IQ_SMEARED_LABEL
348            new_plot.xaxis("\\rm{Q}", 'A^{-1}')
349            new_plot.yaxis("\\rm{Intensity} ","cm^{-1}")
[f0ff8d65]350            # If we have a group ID, use it
351            if pr.info.has_key("plot_group_id"):
[e88ebfd]352              new_plot.group_id = pr.info["plot_group_id"]
[a07e72f]353            new_plot.id = IQ_SMEARED_LABEL
354            new_plot.title = title
[db4ed82]355            self.parent.update_theory(data_id=self.data_id, theory=new_plot)
[f0ff8d65]356            wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot, title=title))
[2a92852]357       
[b659551]358    def _on_pr_npts(self, evt):
359        """
[7116b6e0]360        Redisplay P(r) with a different number of points
[b659551]361        """   
362        from inversion_panel import PrDistDialog
363        dialog = PrDistDialog(None, -1)
364        dialog.set_content(self._pr_npts)
365        if dialog.ShowModal() == wx.ID_OK:
366            self._pr_npts= dialog.get_content()
367            dialog.Destroy()
[feb21d8]368            self.show_pr(self._last_out, self._last_pr, self._last_cov)
[b659551]369        else:
370            dialog.Destroy()
[f3d51f6]371       
372       
373    def show_pr(self, out, pr, cov=None):
[7116b6e0]374        """
[75df58b]375        """     
[f3d51f6]376        # Show P(r)
[b659551]377        x = pylab.arange(0.0, pr.d_max, pr.d_max/self._pr_npts)
[f3d51f6]378   
379        y = numpy.zeros(len(x))
380        dy = numpy.zeros(len(x))
381        y_true = numpy.zeros(len(x))
382
383        sum = 0.0
[feb21d8]384        pmax = 0.0
[dfb58f8]385        cov2 = numpy.ascontiguousarray(cov)
386       
[f3d51f6]387        for i in range(len(x)):
[dfb58f8]388            if cov2==None:
[f3d51f6]389                value = pr.pr(out, x[i])
390            else:
[dfb58f8]391                (value, dy[i]) = pr.pr_err(out, cov2, x[i])
[660b1e6]392            sum += value*pr.d_max/len(x)
[f3d51f6]393           
[feb21d8]394            # keep track of the maximum P(r) value
395            if value>pmax:
396                pmax = value
397               
398            y[i] = value
399               
400        if self._normalize_output==True:
401            y = y/sum
402            dy = dy/sum
403        elif self._scale_output_unity==True:
404            y = y/pmax
405            dy = dy/pmax
[f3d51f6]406       
[dfb58f8]407        if cov2==None:
[a07e72f]408            new_plot = Data1D(x, y)
409            new_plot.symbol = GUIFRAME_ID.CURVE_SYMBOL_NUM
[f3d51f6]410        else:
[a07e72f]411            new_plot = Data1D(x, y, dy=dy)
[f1181977]412        new_plot.name = PR_FIT_LABEL
[f3d51f6]413        new_plot.xaxis("\\rm{r}", 'A')
414        new_plot.yaxis("\\rm{P(r)} ","cm^{-3}")
[75df58b]415        new_plot.title = "P(r) fit"
[a07e72f]416        new_plot.id = PR_FIT_LABEL
[d2ee6f6]417        # Make sure that the plot is linear
[75fbd17]418        new_plot.xtransform = "x"
[a3149c5]419        new_plot.ytransform = "y" 
[e88ebfd]420        new_plot.group_id = "P(r) fit"
[db4ed82]421        self.parent.update_theory(data_id=self.data_id, theory=new_plot)   
[f3d51f6]422        wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot, title="P(r) fit"))
423        return x, pr.d_max
[db4ed82]424         
[75df58b]425    def load(self, data):
[0bae207]426        """
[7116b6e0]427        Load data. This will eventually be replaced
428        by our standard DataLoader class.
[0bae207]429        """
430        class FileData:
431            x = None
432            y = None
433            err = None
434            path = None
435           
436            def __init__(self, path):
437                self.path = path
438               
[75df58b]439        self._current_file_data = FileData(data.path)
[0bae207]440       
[aab8ad7]441        # Use data loader to load file
[75df58b]442        #dataread = Loader().load(path)
443        dataread = data
[ceaf16e]444        # Notify the user if we could not read the file
445        if dataread is None:
446            raise RuntimeError, "Invalid data"
447           
[aab8ad7]448        x = None
449        y = None
450        err = None
451        if dataread.__class__.__name__ == 'Data1D':
452            x = dataread.x
453            y = dataread.y
454            err = dataread.dy
455        else:
[e43c012]456            if isinstance(dataread, list) and len(dataread)>0:
457                x = dataread[0].x
458                y = dataread[0].y
459                err = dataread[0].dy
460                msg = "PrView only allows a single data set at a time. "
461                msg += "Only the first data set was loaded." 
462                wx.PostEvent(self.parent, StatusEvent(status=msg))
463            else:
[6f1f129]464                if dataread is None:
465                    return x, y, err
[e43c012]466                raise RuntimeError, "This tool can only read 1D data"
[aab8ad7]467       
[0bae207]468        self._current_file_data.x = x
469        self._current_file_data.y = y
470        self._current_file_data.err = err
471        return x, y, err
472               
473    def load_columns(self, path = "sphere_60_q0_2.txt"):
474        """
[7116b6e0]475        Load 2- or 3- column ascii
[0bae207]476        """
[f3d51f6]477        import numpy, math, sys
478        # Read the data from the data file
479        data_x   = numpy.zeros(0)
480        data_y   = numpy.zeros(0)
481        data_err = numpy.zeros(0)
[b659551]482        scale    = None
483        min_err  = 0.0
[f3d51f6]484        if not path == None:
485            input_f = open(path,'r')
486            buff    = input_f.read()
487            lines   = buff.split('\n')
488            for line in lines:
489                try:
490                    toks = line.split()
491                    x = float(toks[0])
492                    y = float(toks[1])
[b659551]493                    if len(toks)>2:
494                        err = float(toks[2])
495                    else:
496                        if scale==None:
497                            scale = 0.05*math.sqrt(y)
498                            #scale = 0.05/math.sqrt(y)
499                            min_err = 0.01*y
500                        err = scale*math.sqrt(y)+min_err
501                        #err = 0
502                       
[4a5de6f]503                    data_x = numpy.append(data_x, x)
504                    data_y = numpy.append(data_y, y)
[b659551]505                    data_err = numpy.append(data_err, err)
[f3d51f6]506                except:
[b659551]507                    pass
[f3d51f6]508                   
[357b79b]509        if not scale==None:
510            message = "The loaded file had no error bars, statistical errors are assumed."
511            wx.PostEvent(self.parent, StatusEvent(status=message))
512        else:
513            wx.PostEvent(self.parent, StatusEvent(status=''))
514                       
[b659551]515        return data_x, data_y, data_err     
[f3d51f6]516       
[0bae207]517    def load_abs(self, path):
518        """
[7116b6e0]519        Load an IGOR .ABS reduced file
520       
521        :param path: file path
522       
523        :return: x, y, err vectors
524       
[0bae207]525        """
526        import numpy, math, sys
527        # Read the data from the data file
528        data_x   = numpy.zeros(0)
529        data_y   = numpy.zeros(0)
530        data_err = numpy.zeros(0)
531        scale    = None
532        min_err  = 0.0
533       
534        data_started = False
535        if not path == None:
536            input_f = open(path,'r')
537            buff    = input_f.read()
538            lines   = buff.split('\n')
539            for line in lines:
540                if data_started==True:
541                    try:
542                        toks = line.split()
543                        x = float(toks[0])
544                        y = float(toks[1])
545                        if len(toks)>2:
546                            err = float(toks[2])
547                        else:
548                            if scale==None:
549                                scale = 0.05*math.sqrt(y)
550                                #scale = 0.05/math.sqrt(y)
551                                min_err = 0.01*y
552                            err = scale*math.sqrt(y)+min_err
553                            #err = 0
554                           
555                        data_x = numpy.append(data_x, x)
556                        data_y = numpy.append(data_y, y)
557                        data_err = numpy.append(data_err, err)
558                    except:
559                        pass
560                elif line.find("The 6 columns")>=0:
561                    data_started = True     
562                   
563        if not scale==None:
564            message = "The loaded file had no error bars, statistical errors are assumed."
565            wx.PostEvent(self.parent, StatusEvent(status=message))
566        else:
567            wx.PostEvent(self.parent, StatusEvent(status=''))
568                       
569        return data_x, data_y, data_err     
570       
[f3d51f6]571    def pr_theory(self, r, R):
[7116b6e0]572        """ 
[f3d51f6]573        """
574        if r<=2*R:
575            return 12.0* ((0.5*r/R)**2) * ((1.0-0.5*r/R)**2) * ( 2.0 + 0.5*r/R )
576        else:
577            return 0.0
578
[a07e72f]579    def get_context_menu(self, plotpanel=None):
[f3d51f6]580        """
[7116b6e0]581        Get the context menu items available for P(r)
582       
583        :param graph: the Graph object to which we attach the context menu
584       
585        :return: a list of menu items with call-back function
586       
[f3d51f6]587        """
[a07e72f]588        graph = plotpanel.graph
[660b1e6]589        # Look whether this Graph contains P(r) data
[a07e72f]590        if graph.selected_plottable not in plotpanel.plots:
591            return []
592        item = plotpanel.plots[graph.selected_plottable]
593        if item.id == PR_FIT_LABEL:
594            #add_data_hint = "Load a data file and display it on this plot"
595            #["Add P(r) data",add_data_hint , self._on_add_data],
596            change_n_hint = "Change the number of"
597            change_n_hint += " points on the P(r) output"
598            change_n_label = "Change number of P(r) points"
599            m_list = [[change_n_label, change_n_hint , self._on_pr_npts]]
[feb21d8]600
[a07e72f]601            if self._scale_output_unity or self._normalize_output:
602                hint = "Let the output P(r) keep the scale of the data"
603                m_list.append(["Disable P(r) scaling", hint, 
604                               self._on_disable_scaling])
605            if not self._scale_output_unity:
606                m_list.append(["Scale P_max(r) to unity", 
607                               "Scale P(r) so that its maximum is 1", 
608                               self._on_scale_unity])
609            if not self._normalize_output:
610                m_list.append(["Normalize P(r) to unity", 
611                               "Normalize the integral of P(r) to 1", 
612                               self._on_normalize])
613               
614            return m_list
615             
616        elif item.id in [PR_LOADED_LABEL, IQ_DATA_LABEL, IQ_FIT_LABEL,
617                          IQ_SMEARED_LABEL]:
618            return []
619        elif item.id == graph.selected_plottable:
620               if not self.standalone and issubclass(item.__class__, Data1D):
621                return [["Compute P(r)", 
[75df58b]622                             "Compute P(r) from distribution", 
623                             self._on_context_inversion]]     
[660b1e6]624               
[aa4b8379]625        return []
[660b1e6]626
[feb21d8]627    def _on_disable_scaling(self, evt):
628        """
[7116b6e0]629        Disable P(r) scaling
630           
631        :param evt: Menu event
632       
[feb21d8]633        """
634        self._normalize_output = False
635        self._scale_output_unity = False
636        self.show_pr(self._last_out, self._last_pr, self._last_cov)
637       
[f1181977]638        # Now replot the original added data
639        for plot in self._added_plots:
640            self._added_plots[plot].y = numpy.copy(self._default_Iq[plot])
[75df58b]641            wx.PostEvent(self.parent, 
642                         NewPlotEvent(plot=self._added_plots[plot], 
643                                      title=self._added_plots[plot].name,
[f1181977]644                                                   update=True))       
645       
646        # Need the update flag in the NewPlotEvent to protect against
647        # the plot no longer being there...
648       
[feb21d8]649    def _on_normalize(self, evt):
650        """
[7116b6e0]651        Normalize the area under the P(r) curve to 1.
652        This operation is done for all displayed plots.
653       
654        :param evt: Menu event
655       
[feb21d8]656        """
657        self._normalize_output = True
658        self._scale_output_unity = False
659           
660        self.show_pr(self._last_out, self._last_pr, self._last_cov)
661       
[f1181977]662        # Now scale the added plots too
663        for plot in self._added_plots:
664            sum = numpy.sum(self._added_plots[plot].y)
665            npts = len(self._added_plots[plot].x)
666            sum *= self._added_plots[plot].x[npts-1]/npts
667            y = self._added_plots[plot].y/sum
668           
[a07e72f]669            new_plot = Data1D(self._added_plots[plot].x, y)
670            new_plot.symbol = GUIFRAME_ID.CURVE_SYMBOL_NUM
[e88ebfd]671            new_plot.group_id = self._added_plots[plot].group_id
[a07e72f]672            new_plot.id = self._added_plots[plot].id
673            new_plot.title = self._added_plots[plot].title
[f1181977]674            new_plot.name = self._added_plots[plot].name
675            new_plot.xaxis("\\rm{r}", 'A')
676            new_plot.yaxis("\\rm{P(r)} ","cm^{-3}")
[db4ed82]677            self.parent.update_theory(data_id=self.data_id, theory=new_plot)       
[75df58b]678            wx.PostEvent(self.parent, 
679                         NewPlotEvent(plot=new_plot, update=True,
680                                         title=self._added_plots[plot].name))
[f1181977]681       
[feb21d8]682    def _on_scale_unity(self, evt):
683        """
[7116b6e0]684        Scale the maximum P(r) value on each displayed plot to 1.
685       
686        :param evt: Menu event
687       
[feb21d8]688        """
689        self._scale_output_unity = True
690        self._normalize_output = False
691           
692        self.show_pr(self._last_out, self._last_pr, self._last_cov)
693       
[f1181977]694        # Now scale the added plots too
695        for plot in self._added_plots:
696            _max = 0
697            for y in self._added_plots[plot].y:
698                if y>_max: 
699                    _max = y
700            y = self._added_plots[plot].y/_max
701           
[a07e72f]702            new_plot = Data1D(self._added_plots[plot].x, y)
703            new_plot.symbol = GUIFRAME_ID.CURVE_SYMBOL_NUM
[f1181977]704            new_plot.name = self._added_plots[plot].name
705            new_plot.xaxis("\\rm{r}", 'A')
706            new_plot.yaxis("\\rm{P(r)} ","cm^{-3}")
[db4ed82]707            self.parent.update_theory(data_id=self.data_id,theory=new_plot)       
[75df58b]708            wx.PostEvent(self.parent, 
709                         NewPlotEvent(plot=new_plot, update=True,
710                                title=self._added_plots[plot].name))       
[f1181977]711       
712       
[660b1e6]713    def _on_add_data(self, evt):
714        """
[7116b6e0]715        Add a data curve to the plot
716       
717        :WARNING: this will be removed once guiframe.plotting has
718             its full functionality
[660b1e6]719        """
720        path = self.choose_file()
721        if path==None:
722            return
723       
[14d05ba]724        #x, y, err = self.parent.load_ascii_1D(path)
725        # Use data loader to load file
726        try:
727            dataread = Loader().load(path)
728            x = None
729            y = None
730            err = None
731            if dataread.__class__.__name__ == 'Data1D':
732                x = dataread.x
733                y = dataread.y
734                err = dataread.dy
735            else:
[e43c012]736                if isinstance(dataread, list) and len(dataread)>0:
737                    x = dataread[0].x
738                    y = dataread[0].y
739                    err = dataread[0].dy
740                    msg = "PrView only allows a single data set at a time. "
741                    msg += "Only the first data set was loaded." 
742                    wx.PostEvent(self.parent, StatusEvent(status=msg))
743                else:
[75df58b]744                    msg = "This tool can only read 1D data"
745                    wx.PostEvent(self.parent, StatusEvent(status=msg))
[e43c012]746                    return
747           
[14d05ba]748        except:
749            wx.PostEvent(self.parent, StatusEvent(status=sys.exc_value))
750            return
[660b1e6]751       
[f1181977]752        filename = os.path.basename(path)
[a07e72f]753        new_plot = Data1D(x, y)
754        new_plot.symbol = GUIFRAME_ID.CURVE_SYMBOL_NUM
[f1181977]755        new_plot.name = filename
[660b1e6]756        new_plot.xaxis("\\rm{r}", 'A')
757        new_plot.yaxis("\\rm{P(r)} ","cm^{-3}")
[f1181977]758        # Store a ref to the plottable for later use
759        self._added_plots[filename] = new_plot
760        self._default_Iq[filename]  = numpy.copy(y)
761        wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot, title=filename))
[660b1e6]762       
[f3d51f6]763    def start_thread(self):
[7116b6e0]764        """
765        """
[f3d51f6]766        from pr_thread import CalcPr
767        from copy import deepcopy
768       
769        # If a thread is already started, stop it
770        if self.calc_thread != None and self.calc_thread.isrunning():
771            self.calc_thread.stop()
772               
773        pr = self.pr.clone()
[75df58b]774        self.calc_thread = CalcPr(pr, self.nfunc,
775                                   error_func=self._thread_error, 
776                                   completefn=self._completed, updatefn=None)
[f3d51f6]777        self.calc_thread.queue()
778        self.calc_thread.ready(2.5)
779   
780    def _thread_error(self, error):
[7116b6e0]781        """
782        """
[f3d51f6]783        wx.PostEvent(self.parent, StatusEvent(status=error))
784   
[32dffae4]785    def _estimate_completed(self, alpha, message, elapsed):
[f3d51f6]786        """
[7116b6e0]787        Parameter estimation completed,
788        display the results to the user
789       
790        :param alpha: estimated best alpha
791        :param elapsed: computation time
792       
[f3d51f6]793        """
794        # Save useful info
795        self.elapsed = elapsed
796        self.control_panel.alpha_estimate = alpha
[32dffae4]797        if not message==None:
798            wx.PostEvent(self.parent, StatusEvent(status=str(message)))
[35adaf6]799        self.perform_estimateNT()
800   
801    def _estimateNT_completed(self, nterms, alpha, message, elapsed):
802        """
[7116b6e0]803        Parameter estimation completed,
804        display the results to the user
805       
806        :param alpha: estimated best alpha
807        :param nterms: estimated number of terms
808        :param elapsed: computation time
809       
[35adaf6]810        """
811        # Save useful info
812        self.elapsed = elapsed
813        self.control_panel.nterms_estimate = nterms
814        self.control_panel.alpha_estimate = alpha
815        if not message==None:
816            wx.PostEvent(self.parent, StatusEvent(status=str(message)))
[f3d51f6]817   
818    def _completed(self, out, cov, pr, elapsed):
819        """
[7116b6e0]820        Method called with the results when the inversion
821        is done
822       
823        :param out: output coefficient for the base functions
824        :param cov: covariance matrix
825        :param pr: Invertor instance
826        :param elapsed: time spent computing
827       
[f3d51f6]828        """
[dfb58f8]829        from copy import deepcopy
[f3d51f6]830        # Save useful info
831        self.elapsed = elapsed
[feb21d8]832        # Keep a copy of the last result
833        self._last_pr  = pr.clone()
834        self._last_out = out
835        self._last_cov = cov
836       
[b659551]837        # Save Pr invertor
838        self.pr = pr
839       
[75df58b]840        #message = "Computation completed in"
841        #message +=  %g seconds [chi2=%g]" % (elapsed, pr.chi2)
[d6113849]842        #wx.PostEvent(self.parent, StatusEvent(status=message))
[f3d51f6]843
[dfb58f8]844        cov = numpy.ascontiguousarray(cov)
845
[f3d51f6]846        # Show result on control panel
847        self.control_panel.chi2 = pr.chi2
848        self.control_panel.elapsed = elapsed
849        self.control_panel.oscillation = pr.oscillations(out)
850        #print "OSCILL", pr.oscillations(out)
[d6113849]851        #print "PEAKS:", pr.get_peaks(out)
[dfb58f8]852        self.control_panel.positive = pr.get_positive(out)
853        self.control_panel.pos_err  = pr.get_pos_err(out, cov)
[a4bd2ac]854        self.control_panel.rg = pr.rg(out)
855        self.control_panel.iq0 = pr.iq0(out)
856        self.control_panel.bck = pr.background
[f3d51f6]857       
[aa4b8379]858        if False:
[0bae207]859            for i in range(len(out)):
860                try:
[75df58b]861                    print "%d: %g +- %g" % (i, out[i],
862                                             math.sqrt(math.fabs(cov[i][i])))
[0bae207]863                except: 
864                    print sys.exc_value
865                    print "%d: %g +- ?" % (i, out[i])       
866       
867            # Make a plot of I(q) data
[aa4b8379]868            new_plot = Data1D(self.pr.x, self.pr.y, dy=self.pr.err)
[f1181977]869            new_plot.name = IQ_DATA_LABEL
[aa4b8379]870            new_plot.xaxis("\\rm{Q}", 'A^{-1}')
871            new_plot.yaxis("\\rm{Intensity} ","cm^{-1}")
[a07e72f]872            if pr.info.has_key("plot_group_id"):
[e88ebfd]873                new_plot.group_id = pr.info["plot_group_id"]
[db4ed82]874            new_plot.id = self.data_id
875            self.parent.update_theory(data_id=self.data_id,
[a3149c5]876                                       theory=new_plot)
[aa4b8379]877            wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot, title="Iq"))
[f3d51f6]878               
879        # Show I(q) fit
880        self.show_iq(out, self.pr)
881       
882        # Show P(r) fit
[dfb58f8]883        x_values, x_range = self.show_pr(out, self.pr, cov) 
[f3d51f6]884       
885        # Popup result panel
[75df58b]886        #result_panel = InversionResults(self.parent,
887        #-1, style=wx.RAISED_BORDER)
[f3d51f6]888       
[75df58b]889    def show_data(self, path=None, data=None, reset=False):
[2a92852]890        """
[7116b6e0]891        Show data read from a file
892       
893        :param path: file path
894        :param reset: if True all other plottables will be cleared
895       
[2a92852]896        """
[75df58b]897        #if path is not None:
898        if data is not None:
[ea5551f]899            try:
[75df58b]900                pr = self._create_file_pr(data)
[ceaf16e]901            except:
[75df58b]902                status = "Problem reading data: %s" % sys.exc_value
[ceaf16e]903                wx.PostEvent(self.parent, StatusEvent(status=status))
904                raise RuntimeError, status
[6f1f129]905               
[ceaf16e]906            # If the file contains nothing, just return
907            if pr is None:
908                raise RuntimeError, "Loaded data is invalid"
909           
910            self.pr = pr
[75df58b]911       
[660b1e6]912        # Make a plot of I(q) data
[a07e72f]913        if self.pr.err == None:
914            new_plot = Data1D(self.pr.x, self.pr.y)
915            new_plot.symbol = GUIFRAME_ID.CURVE_SYMBOL_NUM
[357b79b]916        else:
917            new_plot = Data1D(self.pr.x, self.pr.y, dy=self.pr.err)
[f1181977]918        new_plot.name = IQ_DATA_LABEL
[660b1e6]919        new_plot.xaxis("\\rm{Q}", 'A^{-1}')
920        new_plot.yaxis("\\rm{Intensity} ","cm^{-1}")
[aa4b8379]921        new_plot.interactive = True
[e88ebfd]922        new_plot.group_id = IQ_DATA_LABEL
[db4ed82]923        new_plot.id = self.data_id
924        new_plot.title = "I(q)"   
[75df58b]925        wx.PostEvent(self.parent, 
926                     NewPlotEvent(plot=new_plot, title="I(q)", reset=reset))
[660b1e6]927       
[0d88a09]928        self.current_plottable = new_plot
[aa4b8379]929        # Get Q range
930        self.control_panel.q_min = self.pr.x.min()
931        self.control_panel.q_max = self.pr.x.max()
932           
[410aad8]933    def save_data(self, filepath, prstate=None):
934        """
[7116b6e0]935        Save data in provided state object.
936       
937        :TODO: move the state code away from inversion_panel and move it here.
938                Then remove the "prstate" input and make this method private.
939               
940        :param filepath: path of file to write to
941        :param prstate: P(r) inversion state
942       
[410aad8]943        """
944        #TODO: do we need this or can we use DataLoader.loader.save directly?
945       
946        # Add output data and coefficients to state
947        prstate.coefficients = self._last_out
948        prstate.covariance = self._last_cov
949       
950        # Write the output to file
[0ab485b]951        # First, check that the data is of the right type
[75df58b]952        if issubclass(self.current_plottable.__class__,
953                       DataLoader.data_info.Data1D):
[0ab485b]954            self.state_reader.write(filepath, self.current_plottable, prstate)
955        else:
[75df58b]956            msg = "pr.save_data: the data being saved is not a"
957            msg += " DataLoader.data_info.Data1D object" 
958            raise RuntimeError, msg
[410aad8]959       
[660b1e6]960       
[2a92852]961    def setup_plot_inversion(self, alpha, nfunc, d_max, q_min=None, q_max=None, 
962                             bck=False, height=0, width=0):
[7116b6e0]963        """
964        """
[f3d51f6]965        self.alpha = alpha
966        self.nfunc = nfunc
967        self.max_length = d_max
[634f1cf]968        self.q_min = q_min
969        self.q_max = q_max
[a4bd2ac]970        self.has_bck = bck
[2a92852]971        self.slit_height = height
972        self.slit_width  = width
[f3d51f6]973       
[4a5de6f]974        try:
[2a92852]975            pr = self._create_plot_pr()
976            if not pr==None:
977                self.pr = pr
978                self.perform_inversion()
[4a5de6f]979        except:
980            wx.PostEvent(self.parent, StatusEvent(status=sys.exc_value))
[f3d51f6]981
[75df58b]982    def estimate_plot_inversion(self, alpha, nfunc, d_max, 
983                                q_min=None, q_max=None, 
[2a92852]984                                bck=False, height=0, width=0):
[7116b6e0]985        """
986        """
[f3d51f6]987        self.alpha = alpha
988        self.nfunc = nfunc
989        self.max_length = d_max
[634f1cf]990        self.q_min = q_min
991        self.q_max = q_max
[a4bd2ac]992        self.has_bck = bck
[2a92852]993        self.slit_height = height
994        self.slit_width  = width
[f3d51f6]995       
[4a5de6f]996        try:
[2a92852]997            pr = self._create_plot_pr()
998            if not pr==None:
999                self.pr = pr
1000                self.perform_estimate()
[4a5de6f]1001        except:
1002            wx.PostEvent(self.parent, StatusEvent(status=sys.exc_value))           
[f3d51f6]1003
[2a92852]1004    def _create_plot_pr(self, estimate=False):
[f3d51f6]1005        """
[7116b6e0]1006        Create and prepare invertor instance from
1007        a plottable data set.
1008       
1009        :param path: path of the file to read in
1010       
[f3d51f6]1011        """
[ceaf16e]1012        # Sanity check
1013        if self.current_plottable is None:
1014            msg = "Please load a valid data set before proceeding."
1015            wx.PostEvent(self.parent, StatusEvent(status=msg)) 
1016            return None   
1017       
[f3d51f6]1018        # Get the data from the chosen data set and perform inversion
1019        pr = Invertor()
1020        pr.d_max = self.max_length
1021        pr.alpha = self.alpha
[634f1cf]1022        pr.q_min = self.q_min
1023        pr.q_max = self.q_max
[f3d51f6]1024        pr.x = self.current_plottable.x
1025        pr.y = self.current_plottable.y
[a4bd2ac]1026        pr.has_bck = self.has_bck
[2a92852]1027        pr.slit_height = self.slit_height
1028        pr.slit_width = self.slit_width
[f3d51f6]1029       
[d2ee6f6]1030        # Keep track of the plot window title to ensure that
1031        # we can overlay the plots
[e88ebfd]1032        pr.info["plot_group_id"] = self.current_plottable.group_id
[d2ee6f6]1033       
[f3d51f6]1034        # Fill in errors if none were provided
[d2ee6f6]1035        err = self.current_plottable.dy
1036        all_zeros = True
1037        if err == None:
[d483799]1038            err = numpy.zeros(len(pr.y)) 
[d2ee6f6]1039        else:   
1040            for i in range(len(err)):
1041                if err[i]>0:
1042                    all_zeros = False
1043       
1044        if all_zeros:       
1045            scale = None
1046            min_err = 0.0
[f3d51f6]1047            for i in range(len(pr.y)):
[d2ee6f6]1048                # Scale the error so that we can fit over several decades of Q
1049                if scale==None:
1050                    scale = 0.05*math.sqrt(pr.y[i])
1051                    min_err = 0.01*pr.y[i]
1052                err[i] = scale*math.sqrt( math.fabs(pr.y[i]) ) + min_err
[75df58b]1053            message = "The loaded file had no error bars, "
1054            message += "statistical errors are assumed."
[d2ee6f6]1055            wx.PostEvent(self.parent, StatusEvent(status=message))
1056
1057        pr.err = err
[2a92852]1058       
1059        return pr
[f3d51f6]1060
1061         
[75df58b]1062    def setup_file_inversion(self, alpha, nfunc, d_max, data,
1063                             path=None, q_min=None, q_max=None, 
[2a92852]1064                             bck=False, height=0, width=0):
[7116b6e0]1065        """
1066        """
[f3d51f6]1067        self.alpha = alpha
1068        self.nfunc = nfunc
1069        self.max_length = d_max
[634f1cf]1070        self.q_min = q_min
1071        self.q_max = q_max
[a4bd2ac]1072        self.has_bck = bck
[2a92852]1073        self.slit_height = height
1074        self.slit_width  = width
[f3d51f6]1075       
[4a5de6f]1076        try:
[75df58b]1077            #pr = self._create_file_pr(path)
1078            pr = self._create_file_pr(data)
[2a92852]1079            if not pr==None:
1080                self.pr = pr
[4a5de6f]1081                self.perform_inversion()
1082        except:
1083            wx.PostEvent(self.parent, StatusEvent(status=sys.exc_value))
[f3d51f6]1084         
[75df58b]1085    def estimate_file_inversion(self, alpha, nfunc, d_max, data,
1086                                path=None, q_min=None, q_max=None, 
[2a92852]1087                                bck=False, height=0, width=0):
[7116b6e0]1088        """
1089        """
[f3d51f6]1090        self.alpha = alpha
1091        self.nfunc = nfunc
1092        self.max_length = d_max
[634f1cf]1093        self.q_min = q_min
1094        self.q_max = q_max
[a4bd2ac]1095        self.has_bck = bck
[2a92852]1096        self.slit_height = height
1097        self.slit_width  = width
[f3d51f6]1098       
[4a5de6f]1099        try:
[75df58b]1100            pr = self._create_file_pr(data)
1101            #pr = self._create_file_pr(path)
1102            if not pr is None:
[2a92852]1103                self.pr = pr
[4a5de6f]1104                self.perform_estimate()
1105        except:
1106            wx.PostEvent(self.parent, StatusEvent(status=sys.exc_value))
1107               
[f3d51f6]1108         
[75df58b]1109    def _create_file_pr(self, data):
[f3d51f6]1110        """
[7116b6e0]1111        Create and prepare invertor instance from
1112        a file data set.
1113       
1114        :param path: path of the file to read in
1115       
[f3d51f6]1116        """
1117        # Load data
[75df58b]1118        #if os.path.isfile(path):
1119        """   
1120        if self._current_file_data is not None \
1121            and self._current_file_data.path==path:
1122            # Protect against corrupted data from
1123            # previous failed load attempt
1124            if self._current_file_data.x is None:
1125                return None
1126            x = self._current_file_data.x
1127            y = self._current_file_data.y
1128            err = self._current_file_data.err
[357b79b]1129           
[75df58b]1130            message = "The data from this file has already been loaded."
1131            wx.PostEvent(self.parent, StatusEvent(status=message))
1132        else:
1133        """
1134        # Reset the status bar so that we don't get mixed up
1135        # with old messages.
1136        #TODO: refactor this into a proper status handling
1137        wx.PostEvent(self.parent, StatusEvent(status=''))
1138        try:
1139            class FileData:
1140                x = None
1141                y = None
1142                err = None
1143                path = None
1144                def __init__(self, path):
1145                    self.path = path
[6f1f129]1146               
[75df58b]1147            self._current_file_data = FileData(data.path)
1148            self._current_file_data.x = data.x
1149            self._current_file_data.y = data.y
1150            self._current_file_data.err = data.dy
1151            x, y, err = data.x, data.y, data.dy
1152        except:
1153            load_error(sys.exc_value)
1154            return None
1155       
1156        # If the file contains no data, just return
1157        if x is None or len(x) == 0:
1158            load_error("The loaded file contains no data")
1159            return None
1160       
1161        # If we have not errors, add statistical errors
1162        if err is not None and y is not None:
1163            err = numpy.zeros(len(y))
1164            scale = None
1165            min_err = 0.0
1166            for i in range(len(y)):
1167                # Scale the error so that we can fit over several decades of Q
1168                if scale == None:
1169                    scale = 0.05 * math.sqrt(y[i])
1170                    min_err = 0.01 * y[i]
1171                err[i] = scale * math.sqrt(math.fabs(y[i])) + min_err
1172            message = "The loaded file had no error bars, "
1173            message += "statistical errors are assumed."
1174            wx.PostEvent(self.parent, StatusEvent(status=message))
1175       
1176        try:
1177            # Get the data from the chosen data set and perform inversion
1178            pr = Invertor()
1179            pr.d_max = self.max_length
1180            pr.alpha = self.alpha
1181            pr.q_min = self.q_min
1182            pr.q_max = self.q_max
1183            pr.x = x
1184            pr.y = y
1185            pr.err = err
1186            pr.has_bck = self.has_bck
1187            pr.slit_height = self.slit_height
1188            pr.slit_width = self.slit_width
1189            return pr
1190        except:
1191            load_error(sys.exc_value)
[2a92852]1192        return None
[f3d51f6]1193       
1194    def perform_estimate(self):
[7116b6e0]1195        """
1196        """
[f3d51f6]1197        from pr_thread import EstimatePr
1198        from copy import deepcopy
1199       
1200        # If a thread is already started, stop it
[75df58b]1201        if self.estimation_thread != None and \
1202            self.estimation_thread.isrunning():
[f3d51f6]1203            self.estimation_thread.stop()
1204               
1205        pr = self.pr.clone()
[75df58b]1206        self.estimation_thread = EstimatePr(pr, self.nfunc,
1207                                             error_func=self._thread_error, 
1208                                         completefn = self._estimate_completed, 
[f3d51f6]1209                                            updatefn   = None)
1210        self.estimation_thread.queue()
1211        self.estimation_thread.ready(2.5)
[35adaf6]1212   
1213    def perform_estimateNT(self):
[7116b6e0]1214        """
1215        """
[35adaf6]1216        from pr_thread import EstimateNT
1217        from copy import deepcopy
1218       
1219        # If a thread is already started, stop it
1220        if self.estimation_thread != None and self.estimation_thread.isrunning():
1221            self.estimation_thread.stop()
1222               
1223        pr = self.pr.clone()
[2a92852]1224        # Skip the slit settings for the estimation
1225        # It slows down the application and it doesn't change the estimates
1226        pr.slit_height = 0.0
1227        pr.slit_width  = 0.0
[75df58b]1228        self.estimation_thread = EstimateNT(pr, self.nfunc, 
1229                                            error_func=self._thread_error, 
1230                                        completefn = self._estimateNT_completed, 
[35adaf6]1231                                            updatefn   = None)
1232        self.estimation_thread.queue()
1233        self.estimation_thread.ready(2.5)
[f3d51f6]1234       
1235    def perform_inversion(self):
[7116b6e0]1236        """
1237        """
[f3d51f6]1238        # Time estimate
1239        #estimated = self.elapsed*self.nfunc**2
[2a92852]1240        #message = "Computation time may take up to %g seconds" % self.elapsed
1241        #wx.PostEvent(self.parent, StatusEvent(status=message))
[f3d51f6]1242       
1243        # Start inversion thread
1244        self.start_thread()
1245        return
1246       
1247        out, cov = self.pr.lstsq(self.nfunc)
1248       
1249        # Save useful info
1250        self.elapsed = self.pr.elapsed
1251       
1252        for i in range(len(out)):
1253            try:
[75df58b]1254                print "%d: %g +- %g" % (i, out[i],
1255                                         math.sqrt(math.fabs(cov[i][i])))
[f3d51f6]1256            except: 
1257                print "%d: %g +- ?" % (i, out[i])       
1258       
1259        # Make a plot of I(q) data
1260        new_plot = Data1D(self.pr.x, self.pr.y, dy=self.pr.err)
1261        new_plot.name = "I_{obs}(q)"
1262        new_plot.xaxis("\\rm{Q}", 'A^{-1}')
1263        new_plot.yaxis("\\rm{Intensity} ","cm^{-1}")
1264        wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot, title="Iq"))
1265        # Show I(q) fit
1266        self.show_iq(out, self.pr)
1267        # Show P(r) fit
1268        x_values, x_range = self.show_pr(out, self.pr, cov=cov)
1269       
1270    def _on_context_inversion(self, event):
[75df58b]1271        """
1272        """
[f3d51f6]1273        panel = event.GetEventObject()
1274
1275        # If we have more than one displayed plot, make the user choose
[75df58b]1276        if len(panel.plots) > 1 and \
1277            panel.graph.selected_plottable in panel.plots:
[aa4b8379]1278            dataset = panel.graph.selected_plottable
[75df58b]1279        elif len(panel.plots) == 1:
[f3d51f6]1280            dataset = panel.plots.keys()[0]
1281        else:
[75df58b]1282            logging.info("Prview Error: No data is available")
[f3d51f6]1283            return
1284       
1285        # Store a reference to the current plottable
[3fd1ebc]1286        # If we have a suggested value, use it.
1287        try:
1288            estimate = float(self.control_panel.alpha_estimate)
1289            self.control_panel.alpha = estimate
1290        except:
1291            self.control_panel.alpha = self.alpha
[75df58b]1292            logging.info("Prview :Alpha Not estimate yet")
[3fd1ebc]1293            pass
[d6113849]1294        try:
1295            estimate = int(self.control_panel.nterms_estimate)
1296            self.control_panel.nfunc = estimate
1297        except:
1298            self.control_panel.nfunc = self.nfunc
[75df58b]1299            logging.info("Prview : ntemrs Not estimate yet")
[d6113849]1300            pass
[3fd1ebc]1301       
[f3d51f6]1302        self.current_plottable = panel.plots[dataset]
1303        self.control_panel.plotname = dataset
[d6113849]1304        #self.control_panel.nfunc = self.nfunc
[f3d51f6]1305        self.control_panel.d_max = self.max_length
1306        self.parent.set_perspective(self.perspective)
[aa4b8379]1307        self.control_panel._on_invert(None)
[f3d51f6]1308           
1309    def get_panels(self, parent):
1310        """
1311            Create and return a list of panel objects
1312        """
1313        from inversion_panel import InversionControl
1314       
1315        self.parent = parent
[aa4b8379]1316        self.control_panel = InversionControl(self.parent, -1, 
1317                                              style=wx.RAISED_BORDER,
1318                                              standalone=self.standalone)
[f3d51f6]1319        self.control_panel.set_manager(self)
1320        self.control_panel.nfunc = self.nfunc
1321        self.control_panel.d_max = self.max_length
1322        self.control_panel.alpha = self.alpha
1323        self.perspective = []
1324        self.perspective.append(self.control_panel.window_name)
[75df58b]1325     
[f3d51f6]1326        return [self.control_panel]
1327   
[e88ebfd]1328    def set_data(self, data_list=None):
[aa4b8379]1329        """
[75df58b]1330        receive a list of data to compute pr
[aa4b8379]1331        """
[a3149c5]1332        if data_list is None:
1333            data_list = []
[e88ebfd]1334        if len(data_list) >= 1:
1335            if len(data_list) == 1:
1336                data = data_list[0]
[75df58b]1337            else:
[e88ebfd]1338                msg = "Pr panel does not allow multiple Data.\n"
1339                msg += "Please select one!\n"
1340                from pr_widgets import DataDialog
1341                dlg = DataDialog(data_list=data_list, text=msg)
1342                if dlg.ShowModal() == wx.ID_OK:
1343                    data = dlg.get_data()
1344            if data is None:
1345                return
1346            if issubclass(data.__class__, Data1D):
1347                try:
[db4ed82]1348                    self.data_id = data.id
[e88ebfd]1349                    self.control_panel._change_file(evt=None, data=data)
1350                except:
1351                     msg = "Prview Set_data: " + str(sys.exc_value)
1352                     wx.PostEvent(self.parent, StatusEvent(status=msg,
1353                                                            info="error"))
1354            else:   
1355                msg = "Pr cannot be computed for data of "
1356                msg += "type %s" % (data_list[0].__class__.__name__)
[75a7ece]1357                wx.PostEvent(self.parent, 
[e88ebfd]1358                         StatusEvent(status=msg, info='error'))
[75a7ece]1359        else:
1360            msg = "Pr contain no data"
1361            wx.PostEvent(self.parent, StatusEvent(status=msg, info='warning'))
[75df58b]1362           
[f3d51f6]1363    def post_init(self):
1364        """
1365            Post initialization call back to close the loose ends
1366            [Somehow openGL needs this call]
1367        """
[3e41f43]1368        if self.standalone:
[d2ee6f6]1369            self.parent.set_perspective(self.perspective)
[35adaf6]1370 
1371if __name__ == "__main__":
1372    i = Plugin()
1373    print i.perform_estimateNT()
1374   
1375   
1376   
[660b1e6]1377   
Note: See TracBrowser for help on using the repository browser.