source: sasview/src/sans/perspectives/pr/pr.py @ f3efa09

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 f3efa09 was f3efa09, checked in by butler, 9 years ago

Fix Pr failure ticket #273


Long version: In August 2013 it was determined that many instability problems experienced by SasView? were do to a threading problem in fitting.py. Mainly the authors seemed to not realize that calling thread.stop just raised a flag and that the thread itself needed to check for said flag regularly and kill the thread if it found the flag raised. A quick fix was implemented by adding a wait till the existing thread ended naturally to the two places in the code where the stop is raised. This seems to work.

At the same time pr.py was found to use the same raising of the stop flag three times and it was assumed that the same problem existed and the same wait “fix” was implemented. Subsequenty it has been shown that the new wait code in pr.py NEVER gets reached and thus the fix breaks the PrView? perspective. It may be that this earlier code properly dealt with threads properly(still remembering how the thread code was meant to be used). Whatever the case will remove this fix for now.

Finally another thread.stop was found once in simulation.py. since this is older code it may also be handled properly so am removing the “Fix” from that as well for now.

to quickly find these points in the future:
fitting.py - two instances that seem to need fixing
pr.py - three instances that may work correctly
simulation.py - one instace

search terms:
“-PDB” or
“d.stop” (thread.stop or thread_1D.stop or 2D.stop)

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