source: sasview/prview/perspectives/pr/pr.py @ 45f896f

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 45f896f was 45f896f, checked in by Jae Cho <jhjcho@…>, 13 years ago

fixed the compute Pr from popup menu in plot panel

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