source: sasview/inversionview/src/sans/perspectives/pr/pr.py @ 8b1d759

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 8b1d759 was 6813da7d, checked in by Jae Cho <jhjcho@…>, 13 years ago

dialog fixes

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