source: sasview/src/sas/sasgui/perspectives/pr/pr.py @ c10d9d6c

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 c10d9d6c was c10d9d6c, checked in by Piotr Rozyczko <piotr.rozyczko@…>, 8 years ago

updated references to old paths.

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