source: sasview/sansview/perspectives/fitting/fitting.py @ 0fd2f27

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 0fd2f27 was 0fd2f27, checked in by Gervaise Alina <gervyh@…>, 13 years ago

make sure code do not pass the pylint printing margin

  • Property mode set to 100644
File size: 71.0 KB
Line 
1
2
3################################################################################
4#This software was developed by the University of Tennessee as part of the
5#Distributed Data Analysis of Neutron Scattering Experiments (DANSE)
6#project funded by the US National Science Foundation.
7#
8#See the license text in license.txt
9#
10#copyright 2009, University of Tennessee
11################################################################################
12
13
14import re
15import sys
16import wx
17import logging
18import numpy
19import string
20import time
21from copy import deepcopy
22import models
23import fitpage
24
25
26from DataLoader.loader import Loader
27from sans.guiframe.dataFitting import Data2D
28from sans.guiframe.dataFitting import Data1D
29from sans.guiframe.events import NewPlotEvent
30from sans.guiframe.events import StatusEvent 
31from sans.guiframe.events import EVT_SLICER_PANEL
32from sans.guiframe.events import EVT_SLICER_PARS_UPDATE
33from sans.guiframe.gui_style import GUIFRAME_ID
34from sans.guiframe.plugin_base import PluginBase
35
36from .console import ConsoleUpdate
37from .fitproblem import FitProblem
38from .fitpanel import FitPanel
39from .fit_thread import FitThread
40from .pagestate import Reader
41from .fitpage import Chi2UpdateEvent
42
43DEFAULT_BEAM = 0.005
44DEFAULT_QMIN = 0.001
45DEFAULT_QMAX = 0.13
46DEFAULT_NPTS = 50
47MAX_NBR_DATA = 4
48SANS_F_TOL = 5e-05
49
50(PageInfoEvent, EVT_PAGE_INFO)   = wx.lib.newevent.NewEvent()
51
52if sys.platform.count("darwin")==0:
53    ON_MAC = False
54else:
55    ON_MAC = True   
56
57class Plugin(PluginBase):
58    """
59    Fitting plugin is used to perform fit
60    """
61    def __init__(self, standalone=False):
62        PluginBase.__init__(self, name="Fitting", standalone=standalone)
63       
64        #list of panel to send to guiframe
65        self.mypanels = []
66        # reference to the current running thread
67        self.calc_2D = None
68        self.calc_1D = None
69        self.fit_thread_list = {}
70        self.residuals = None
71        self.fit_panel = None
72        # Start with a good default
73        self.elapsed = 0.022
74        # the type of optimizer selected, park or scipy
75        self.fitter  = None
76        self.fit_panel = None
77        #let fit ready
78        self.fitproblem_count = None
79        #Flag to let the plug-in know that it is running stand alone
80        self.standalone = True
81        ## dictionary of page closed and id
82        self.closed_page_dict = {}
83        ## Fit engine
84        self._fit_engine = 'scipy'
85        ## Relative error desired in the sum of squares (float); scipy only
86        self.ftol = SANS_F_TOL
87        #List of selected data
88        self.selected_data_list = []
89        ## list of slicer panel created to display slicer parameters and results
90        self.slicer_panels = []
91        # model 2D view
92        self.model2D_id = None
93        #keep reference of the simultaneous fit page
94        self.sim_page = None
95        self.index_model = 0
96        #Create a reader for fit page's state
97        self.state_reader = None 
98        self._extensions = '.fitv'
99        self.scipy_id = wx.NewId()
100        self.park_id = wx.NewId()
101       
102        self.temp_state = []
103        self.state_index = 0
104        self.sfile_ext = None
105        # take care of saving  data, model and page associated with each other
106        self.page_finder = {}
107        # Log startup
108        logging.info("Fitting plug-in started") 
109       
110    def on_batch_selection(self, flag):
111        """
112        switch the the notebook of batch mode or not
113        """
114        self.batch_on = flag
115        if self.batch_on:
116            if self.fit_panel is not None:
117                self.fit_panel.batch_on = flag
118       
119    def populate_menu(self, owner):
120        """
121        Create a menu for the Fitting plug-in
122       
123        :param id: id to create a menu
124        :param owner: owner of menu
125       
126        :return: list of information to populate the main menu
127       
128        """
129        #Menu for fitting
130        self.menu1 = wx.Menu()
131        id1 = wx.NewId()
132        simul_help = "Add new fit panel"
133        self.menu1.Append(id1, '&New Fit Page',simul_help)
134        wx.EVT_MENU(owner, id1, self.on_add_new_page)
135        self.menu1.AppendSeparator()
136        id1 = wx.NewId()
137        simul_help = "Simultaneous Fit"
138        self.menu1.Append(id1, '&Simultaneous Fit',simul_help)
139        wx.EVT_MENU(owner, id1, self.on_add_sim_page)
140        self.menu1.AppendSeparator()
141        #Set park engine
142       
143        scipy_help= "Scipy Engine: Perform Simple fit. More in Help window...."
144        self.menu1.AppendCheckItem(self.scipy_id, "Simple FitEngine [LeastSq]",
145                                   scipy_help) 
146        wx.EVT_MENU(owner, self.scipy_id,  self._onset_engine_scipy)
147       
148        park_help = "Park Engine: Perform Complex fit. More in Help window...."
149        self.menu1.AppendCheckItem(self.park_id, "Complex FitEngine [ParkMC]",
150                                   park_help) 
151        wx.EVT_MENU(owner, self.park_id,  self._onset_engine_park)
152       
153        self.menu1.FindItemById(self.scipy_id).Check(True)
154        self.menu1.FindItemById(self.park_id).Check(False)
155        self.menu1.AppendSeparator()
156        self.id_tol = wx.NewId()
157        ftol_help = "Change the current FTolerance (=%s) " % str(self.ftol)
158        ftol_help += "of Simple FitEngine..." 
159        self.menu1.Append(self.id_tol, "Change FTolerance [LeastSq Only]", 
160                                   ftol_help) 
161        wx.EVT_MENU(owner, self.id_tol,  self.show_ftol_dialog)
162       
163       
164        #create  menubar items
165        return [(self.menu1, self.sub_menu)]
166               
167    def on_add_sim_page(self, event):
168        """
169        Create a page to access simultaneous fit option
170        """
171        if self.sim_page != None:
172            msg= "Simultaneous Fit page already opened"
173            wx.PostEvent(self.parent, StatusEvent(status= msg))
174            return 
175       
176        self.sim_page= self.fit_panel.add_sim_page()
177       
178    def help(self, evt):
179        """
180        Show a general help dialog.
181        """
182        from help_panel import  HelpWindow
183        frame = HelpWindow(None, -1, 'HelpWindow')   
184        frame.Show(True)
185       
186    def get_context_menu(self, plotpanel=None):
187        """
188        Get the context menu items available for P(r).them allow fitting option
189        for Data2D and Data1D only.
190       
191        :param graph: the Graph object to which we attach the context menu
192       
193        :return: a list of menu items with call-back function
194       
195        :note: if Data1D was generated from Theory1D 
196                the fitting option is not allowed
197               
198        """
199        graph = plotpanel.graph
200        fit_option = "Select data for fitting"
201        fit_hint =  "Dialog with fitting parameters "
202       
203        if graph.selected_plottable not in plotpanel.plots:
204            return []
205        item = plotpanel.plots[graph.selected_plottable]
206        if item.__class__.__name__ is "Data2D": 
207            if hasattr(item,"is_data"):
208                if item.is_data:
209                    return [[fit_option, fit_hint, self._onSelect]]
210                else:
211                    return [] 
212            return [[fit_option, fit_hint, self._onSelect]]
213        else:
214           
215            # if is_data is true , this in an actual data loaded
216            #else it is a data created from a theory model
217            if hasattr(item,"is_data"):
218                if item.is_data:
219                    return [[fit_option, fit_hint,
220                              self._onSelect]]
221                else:
222                    return [] 
223        return []   
224
225
226    def get_panels(self, parent):
227        """
228        Create and return a list of panel objects
229        """
230        self.parent = parent
231        #self.parent.Bind(EVT_FITSTATE_UPDATE, self.on_set_state_helper)
232        # Creation of the fit panel
233        self.fit_panel = FitPanel(parent=self.parent, manager=self)
234        self.on_add_new_page(event=None)
235        #Set the manager for the main panel
236        self.fit_panel.set_manager(self)
237        # List of windows used for the perspective
238        self.perspective = []
239        self.perspective.append(self.fit_panel.window_name)
240       
241        #index number to create random model name
242        self.index_model = 0
243        self.index_theory= 0
244        self.parent.Bind(EVT_SLICER_PANEL, self._on_slicer_event)
245        self.parent.Bind(EVT_SLICER_PARS_UPDATE, self._onEVT_SLICER_PANEL)
246        self.parent._mgr.Bind(wx.aui.EVT_AUI_PANE_CLOSE,self._onclearslicer)   
247        #Create reader when fitting panel are created
248        self.state_reader = Reader(self.set_state)   
249        #append that reader to list of available reader
250        loader = Loader()
251        loader.associate_file_reader(".fitv", self.state_reader)
252        #loader.associate_file_reader(".svs", self.state_reader)
253        #from sans.perspectives.calculator.sld_panel import SldPanel
254        #Send the fitting panel to guiframe
255        self.mypanels.append(self.fit_panel) 
256        #self.mypanels.append(SldPanel(parent=self.parent, base=self.parent))
257        return self.mypanels
258   
259    def clear_panel(self):
260        """
261        """
262        self.fit_panel.clear_panel()
263       
264    def set_default_perspective(self):
265        """
266        Call back method that True to notify the parent that the current plug-in
267        can be set as default  perspective.
268        when returning False, the plug-in is not candidate for an automatic
269        default perspective setting
270        """
271        return True
272   
273    def delete_data(self, data):
274        """
275        delete  the given data from panel
276        """
277        self.fit_panel.delete_data(data)
278       
279    def set_data(self, data_list=None):
280        """
281        receive a list of data to fit
282        """
283        if data_list is None:
284            data_list = []
285        selected_data_list = []
286        if self.batch_on:
287            page = self.add_fit_page(data=data_list)
288        else:
289            if len(data_list) > MAX_NBR_DATA:
290                from fitting_widgets import DataDialog
291                dlg = DataDialog(data_list=data_list, nb_data=MAX_NBR_DATA)
292                if dlg.ShowModal() == wx.ID_OK:
293                    selected_data_list = dlg.get_data()
294                dlg.Destroy()
295               
296            else:
297                selected_data_list = data_list
298            try:
299                for data in selected_data_list:
300                    page = self.add_fit_page(data=[data])
301            except:
302                msg = "Fitting Set_data: " + str(sys.exc_value)
303                wx.PostEvent(self.parent, StatusEvent(status=msg, info="error"))
304   
305    def set_top_panel(self):
306        """
307        Close default (welcome) panel
308        """
309        if 'default' in self.parent.panels:
310            self.parent.on_close_welcome_panel()
311
312             
313    def set_theory(self,  theory_list=None):
314        """
315        """
316        #set the model state for a given theory_state:
317        for item in theory_list:
318            try:
319                _, theory_state = item
320                self.fit_panel.set_model_state(theory_state)
321            except:
322                msg = "Fitting: cannot deal with the theory received"
323                logging.error("set_theory " + msg + "\n" + str(sys.exc_value))
324                wx.PostEvent(self.parent, StatusEvent(status=msg, info="error"))
325           
326    def set_state(self, state=None, datainfo=None, format=None):
327        """
328        Call-back method for the fit page state reader.
329        This method is called when a .fitv/.svs file is loaded.
330       
331        : param state: PageState object
332        : param datainfo: data
333        """
334        #state = self.state_reader.get_state()
335        if state != None:
336            state = state.clone()
337            # store fitting state in temp_state
338            self.temp_state.append(state) 
339        else:
340            self.temp_state = []
341        # index to start with for a new set_state
342        self.state_index = 0
343        # state file format
344        self.sfile_ext = format
345       
346        self.on_set_state_helper(event=None)
347
348    def  on_set_state_helper(self,event=None):
349        """
350        Set_state_helper. This actually sets state
351        after plotting data from state file.
352       
353        : event: FitStateUpdateEvent called
354            by dataloader.plot_data from guiframe
355        """
356        if len(self.temp_state) == 0:
357            if self.state_index==0 and len(self.mypanels) <= 0 \
358            and self.sfile_ext =='.svs':
359                self.fit_panel.add_default_pages()
360                self.temp_state = []
361                self.state_index = 0
362            return
363       
364        try:
365            # Load fitting state
366            state = self.temp_state[self.state_index]
367            #panel state should have model selection to set_state
368            if state.formfactorcombobox != None:
369                #set state
370                data = self.parent.create_gui_data(state.data)
371                data.group_id = state.data.group_id
372                self.parent.add_data(data_list={data.id:data})
373                wx.PostEvent(self.parent, NewPlotEvent(plot=data,
374                                        title=data.title))
375                #need to be fix later make sure we are sendind guiframe.data
376                #to panel
377                state.data = data
378                page = self.fit_panel.set_state(state)   
379            else:
380                #just set data because set_state won't work
381                data = self.parent.create_gui_data(state.data)
382                data.group_id = state.data.group_id
383                self.parent.add_data(data_list={data.id:data})
384                wx.PostEvent(self.parent, NewPlotEvent(plot=data,
385                                        title=data.title))
386                page = self.add_fit_page([data])
387                caption = page.window_name
388                self.store_data(uid=page.uid, data=page.get_data(),
389                        data_list=page.get_data_list(), 
390                        caption=page.window_name)
391                self.mypanels.append(page) 
392               
393            # get ready for the next set_state
394            self.state_index += 1
395
396            #reset state variables to default when all set_state is finished.
397            if len(self.temp_state) == self.state_index:
398               
399                self.temp_state = []
400                #self.state_index = 0
401                # Make sure the user sees the fitting panel after loading
402                #self.parent.set_perspective(self.perspective)
403                self.on_perspective(event=None)
404        except:
405            self.state_index==0
406            self.temp_state = []
407            raise
408                 
409    def save_fit_state(self, filepath, fitstate): 
410        """
411        save fit page state into file
412        """
413        self.state_reader.write(filename=filepath, fitstate=fitstate)
414       
415    def set_fit_range(self, uid, qmin, qmax):
416        """
417        Set the fitting range of a given page
418        """
419        self.page_finder[uid].set_range(qmin=qmin, qmax=qmax)
420                   
421    def schedule_for_fit(self,value=0, uid=None,fitproblem =None): 
422        """
423        Set the fit problem field to 0 or 1 to schedule that problem to fit.
424        Schedule the specified fitproblem or get the fit problem related to
425        the current page and set value.
426       
427        :param value: integer 0 or 1
428        :param fitproblem: fitproblem to schedule or not to fit
429       
430        """   
431        if fitproblem !=None:
432            fitproblem.schedule_tofit(value)
433        else:
434            self.page_finder[uid].schedule_tofit(value)
435         
436    def get_page_finder(self):
437        """
438        return self.page_finder used also by simfitpage.py
439        """ 
440        return self.page_finder
441   
442    def set_page_finder(self,modelname,names,values):
443        """
444        Used by simfitpage.py to reset a parameter given the string constrainst.
445         
446        :param modelname: the name ot the model for with the parameter
447                            has to reset
448        :param value: can be a string in this case.
449        :param names: the paramter name
450         
451        :note: expecting park used for fit.
452         
453        """ 
454        sim_page_id = self.sim_page.uid
455        for uid, value in self.page_finder.iteritems():
456            if uid != sim_page_id:
457                list = value.get_model()
458                model = list[0]
459                if model.name == modelname:
460                    value.set_model_param(names, values)
461                    break
462         
463    def split_string(self,item): 
464        """
465        receive a word containing dot and split it. used to split parameterset
466        name into model name and parameter name example: ::
467       
468            paramaterset (item) = M1.A
469            Will return model_name = M1 , parameter name = A
470           
471        """
472        if string.find(item,".")!=-1:
473            param_names= re.split("\.",item)
474            model_name=param_names[0]           
475            ##Assume max len is 3; eg., M0.radius.width
476            if len(param_names) == 3:
477                param_name=param_names[1]+"."+param_names[2]
478            else:
479                param_name=param_names[1]                   
480            return model_name,param_name
481   
482    def set_ftol(self, ftol=None):
483        """
484        Set ftol: Relative error desired in the sum of chi squares. 
485        """
486        # check if it is flaot
487        try:
488            f_tol = float(ftol)
489        except:
490            # default
491            f_tol = SANS_F_TOL
492           
493        self.ftol = f_tol
494        # update ftol menu help strings
495        ftol_help = "Change the current FTolerance (=%s) " % str(self.ftol)
496        ftol_help += "of Simple FitEngine..." 
497        self.menu1.SetHelpString(self.id_tol, ftol_help)
498       
499    def show_ftol_dialog(self, event=None):
500        """
501        Dialog to select ftol for Scipy
502        """
503        #if event != None:
504        #    event.Skip()
505        from ftol_dialog import ChangeFtol
506        panel = ChangeFtol(self.parent, self)
507        panel.ShowModal()
508                 
509    def stop_fit(self, uid):
510        """
511        Stop the fit engine
512        """
513        if uid in self.fit_thread_list.keys():
514            calc_fit = self.fit_thread_list[uid]
515            if calc_fit is not  None and calc_fit.isrunning():
516                calc_fit.stop()
517                msg = "Fit stop!"
518                wx.PostEvent(self.parent, StatusEvent(status=msg, type="stop"))
519            del self.fit_thread_list[uid]
520        #set the fit button label of page when fit stop is trigger from
521        #simultaneous fit pane
522        if  self.sim_page is not None and uid == self.sim_page.uid:
523            for uid, value in self.page_finder.iteritems():
524                if value.get_scheduled() == 1:
525                    if uid in self.fit_panel.opened_pages.keys():
526                        panel = self.fit_panel.opened_pages[uid]
527                        panel. _on_fit_complete()
528 
529    def set_smearer(self, uid, smearer, qmin=None, qmax=None, draw=True, 
530                    enable2D=False):
531        """
532        Get a smear object and store it to a fit problem
533       
534        :param smearer: smear object to allow smearing data
535       
536        """   
537        if uid not in self.page_finder.keys():
538            msg = "Cannot find ID: %s in page_finder" % str(uid)
539            raise ValueError, msg
540        self.page_finder[uid].set_smearer(smearer)
541        self.page_finder[uid].set_enable2D(enable2D)
542        if draw:
543            ## draw model 1D with smeared data
544            data =  self.page_finder[uid].get_fit_data()
545            model = self.page_finder[uid].get_model()
546            if model is None:
547                return
548            enable1D = True
549            enable2D = self.page_finder[uid].get_enable2D()
550            if enable2D:
551                enable1D = False
552
553            ## if user has already selected a model to plot
554            ## redraw the model with data smeared
555            smear = self.page_finder[uid].get_smearer()
556            self.draw_model(model=model, data=data, page_id=uid, smearer=smear,
557                enable1D=enable1D, enable2D=enable2D,
558                qmin=qmin, qmax=qmax)
559            self._mac_sleep(0.2)
560           
561    def _mac_sleep(self, sec=0.2):
562        """
563        Give sleep to MAC
564        """
565        if ON_MAC:
566           time.sleep(sec)
567               
568    def draw_model(self, model, page_id, data=None, smearer=None,
569                   enable1D=True, enable2D=False,
570                   state=None,
571                   toggle_mode_on=False,
572                   qmin=DEFAULT_QMIN, qmax=DEFAULT_QMAX, 
573                   qstep=DEFAULT_NPTS,
574                   update_chisqr=True):
575        """
576        Draw model.
577       
578        :param model: the model to draw
579        :param name: the name of the model to draw
580        :param data: the data on which the model is based to be drawn
581        :param description: model's description
582        :param enable1D: if true enable drawing model 1D
583        :param enable2D: if true enable drawing model 2D
584        :param qmin:  Range's minimum value to draw model
585        :param qmax:  Range's maximum value to draw model
586        :param qstep: number of step to divide the x and y-axis
587        :param update_chisqr: update chisqr [bool]
588             
589        """
590        if data.__class__.__name__ == "Data1D" or not enable2D:   
591            ## draw model 1D with no loaded data
592            self._draw_model1D(model=model, 
593                               data=data,
594                               page_id=page_id,
595                               enable1D=enable1D, 
596                               smearer=smearer,
597                               qmin=qmin,
598                               qmax=qmax, 
599                               toggle_mode_on=toggle_mode_on,
600                               state=state,
601                               qstep=qstep,
602                               update_chisqr=update_chisqr)
603        else:     
604            ## draw model 2D with no initial data
605            self._draw_model2D(model=model,
606                                page_id=page_id,
607                                data=data,
608                                enable2D=enable2D,
609                                smearer=smearer,
610                                qmin=qmin,
611                                qmax=qmax,
612                                state=state,
613                                toggle_mode_on=toggle_mode_on,
614                                qstep=qstep,
615                                update_chisqr=update_chisqr)
616           
617    def onFit(self, uid=None):
618        """
619        perform fit
620        """
621        ##  count the number of fitproblem schedule to fit
622        fitproblem_count = 0
623        for value in self.page_finder.values():
624            if value.get_scheduled() == 1:
625                fitproblem_count += 1
626               
627        ## if simultaneous fit change automatically the engine to park
628        if fitproblem_count > 1:
629            self._on_change_engine(engine='park')
630           
631        self.fitproblem_count = fitproblem_count 
632         
633        from sans.fit.Fitting import Fit
634        fitter = Fit(self._fit_engine)
635       
636        if self._fit_engine == "park":
637            engineType = "Simultaneous Fit"
638        else:
639            engineType = "Single Fit"
640           
641        fitter_list = []
642        fproblemId = 0
643        self.current_pg = None
644        list_page_id = []
645        for page_id, value in self.page_finder.iteritems():
646            # For simulfit (uid give with None), do for-loop
647            # if uid is specified (singlefit), do it only on the page.
648            if engineType == "Single Fit":
649                if page_id != uid:
650                    continue
651            try:
652                if value.get_scheduled() == 1:
653                    #Get list of parameters name to fit
654                    pars = []
655                    templist = []
656                   
657                    page = self.fit_panel.get_page_by_id(page_id)
658                    templist = page.get_param_list()
659                    # missing fit parameters
660                    #if not templist:
661                    #    return
662                    # have the list
663                    for element in templist:
664                        name = str(element[1])
665                        pars.append(name)
666                    #Set Engine  (model , data) related to the page on
667                    self._fit_helper(value=value, pars=pars,
668                                     fitter=fitter,
669                                     fitter_list=fitter_list,
670                                      fitproblem_id=fproblemId,
671                                      title=engineType) 
672                    list_page_id.append(page_id)
673                    fproblemId += 1 
674                    current_page_id = page_id
675            except:
676                #raise
677                msg= "%s error: %s" % (engineType, sys.exc_value)
678                wx.PostEvent(self.parent, StatusEvent(status=msg, info="error",
679                                                      type="stop"))
680                return 
681        ## If a thread is already started, stop it
682        #if self.calc_fit!= None and self.calc_fit.isrunning():
683        #    self.calc_fit.stop()
684        msg = "Fitting is in progress..."
685        wx.PostEvent( self.parent, StatusEvent(status=msg, type="progress" ))
686       
687        #Handler used for park engine displayed message
688        handler = ConsoleUpdate(parent=self.parent,
689                                manager=self,
690                                improvement_delta=0.1)
691       
692        self._mac_sleep(0.2)
693        ## perform single fit
694        if fitproblem_count == 1:
695            print "fitter_list", fitter_list
696            calc_fit = FitThread(handler = handler,
697                                    fn=fitter_list,
698                                    pars=pars,
699                                    page_id=list_page_id,
700                                    completefn=self._single_fit_completed,
701                                    ftol=self.ftol)
702        else:
703            current_page_id = self.sim_page.uid
704            ## Perform more than 1 fit at the time
705            calc_fit = FitThread(handler=handler,
706                                    fn=fitter_list,
707                                    page_id=list_page_id,
708                                    updatefn=handler.update_fit,
709                                    completefn=self._simul_fit_completed,
710                                    ftol=self.ftol)
711        self.fit_thread_list[current_page_id] = calc_fit
712        calc_fit.queue()
713        msg = "Fitting is in progress..."
714        wx.PostEvent( self.parent, StatusEvent(status=msg, type="progress" ))
715       
716        self.ready_fit(calc_fit=calc_fit)
717       
718    def ready_fit(self, calc_fit):
719        """
720        Ready for another fit
721        """
722        if self.fitproblem_count != None and self.fitproblem_count > 1:
723            calc_fit.ready(2.5)
724           
725        else:
726            time.sleep(0.4)
727           
728    def remove_plot(self, uid, theory=False):
729        """
730        remove model plot when a fit page is closed
731        """
732        fitproblem = self.page_finder[uid]
733        data = fitproblem.get_fit_data()
734        model = fitproblem.get_model()
735        plot_id = None
736        if model is not None:
737            plot_id = data.id + name
738        if theory:
739            plot_id = data.id
740        group_id = data.group_id
741        wx.PostEvent(self.parent, NewPlotEvent(id=plot_id,
742                                                   group_id=group_id,
743                                                   action='remove'))
744           
745    def store_data(self, uid, data=None, data_list=None, caption=None):
746        """
747        Helper to save page reference into the plug-in
748       
749        :param page: page to store
750       
751        """
752        if data_list is None:
753            data_list = []
754        #create a fitproblem storing all link to data,model,page creation
755        if not uid in self.page_finder.keys():
756            self.page_finder[uid] = FitProblem()
757        self.page_finder[uid].set_fit_data(data)
758        self.page_finder[uid].set_fit_data_list(data_list)
759        self.page_finder[uid].set_fit_tab_caption(caption)
760       
761    def on_add_new_page(self, event=None):
762        """
763        ask fit panel to create a new empty page
764        """
765        try:
766            page = self.fit_panel.add_empty_page()
767            page_caption = page.window_name
768            # add data associated to the page created
769            if page != None: 
770                self.store_data(uid=page.uid, caption=page_caption,
771                                data=page.get_data())
772                wx.PostEvent(self.parent, StatusEvent(status="Page Created",
773                                               info="info"))
774            else:
775                msg = "Page was already Created"
776                wx.PostEvent(self.parent, StatusEvent(status=msg,
777                                                       info="warning"))
778            self.set_top_panel()
779        except:
780            raise
781            #msg = "Creating Fit page: %s"%sys.exc_value
782            #wx.PostEvent(self.parent, StatusEvent(status=msg, info="error"))
783       
784       
785    def add_fit_page(self, data):
786        """
787        given a data, ask to the fitting panel to create a new fitting page,
788        get this page and store it into the page_finder of this plug-in
789        """
790        page = self.fit_panel.set_data(data)
791        page_caption = page.window_name
792        #append Data1D to the panel containing its theory
793        #if theory already plotted
794        if page.uid in self.page_finder:
795            theory_data = self.page_finder[page.uid].get_theory_data()
796            data = page.get_data()
797            if issubclass(data.__class__, Data2D):
798                data.group_id = wx.NewId()
799                if theory_data is not None:
800                    group_id = str(page.uid) + " Model1D"
801                    wx.PostEvent(self.parent, 
802                             NewPlotEvent(group_id=group_id,
803                                               action="delete"))
804                    self.parent.update_data(prev_data=theory_data,
805                                             new_data=data)     
806            else:
807                if theory_data is not None:
808                    group_id = str(page.uid) + " Model2D"
809                    data.group_id = theory_data.group_id
810                    wx.PostEvent(self.parent, 
811                             NewPlotEvent(group_id=group_id,
812                                               action="delete"))
813                    self.parent.update_data(prev_data=theory_data,
814                                             new_data=data)   
815             
816        self.store_data(uid=page.uid, data=page.get_data(),
817                        data_list=page.get_data_list(), 
818                        caption=page.window_name)
819        if self.sim_page is not None:
820            self.sim_page.draw_page()
821        return page
822           
823    def _onEVT_SLICER_PANEL(self, event):
824        """
825        receive and event telling to update a panel with a name starting with
826        event.panel_name. this method update slicer panel for a given interactor.
827       
828        :param event: contains type of slicer , paramaters for updating the panel
829            and panel_name to find the slicer 's panel concerned.
830        """
831        for item in self.parent.panels:
832            name = event.panel_name
833            if self.parent.panels[item].window_caption.startswith(name):
834                self.parent.panels[item].set_slicer(event.type, event.params)
835               
836        self.parent._mgr.Update()
837   
838    def _closed_fitpage(self, event):   
839        """
840        request fitpanel to close a given page when its unique data is removed
841        from the plot. close fitpage only when the a loaded data is removed
842        """   
843        if event is None or event.data is None:
844            return
845        if hasattr(event.data,"is_data"):
846            if not event.data.is_data or \
847                event.data.__class__.__name__ == "Data1D":
848                self.fit_panel.close_page_with_data(event.data) 
849       
850    def _add_page_onmenu(self, name, fitproblem=None):
851        """
852        Add name of a closed page of fitpanel in a menu
853        """
854        list = self.menu1.GetMenuItems()
855        for item in list:
856            if name == item.GetItemLabel():
857                self.closed_page_dict[name][1] = fitproblem
858               
859        if not name in self.closed_page_dict.keys():   
860            # Post paramters
861            event_id = wx.NewId()
862            self.menu1.Append(event_id, name, "Show %s fit panel" % name)
863            self.closed_page_dict[name]= [event_id, fitproblem]
864            wx.EVT_MENU(self.parent, event_id, self._open_closed_page)
865       
866    def _open_closed_page(self, event):   
867        """
868        reopen a closed page
869        """
870        for name, value in self.closed_page_dict.iteritems():
871            if event.GetId() in value:
872                uid,fitproblem = value
873                if name !="Model":
874                    data= fitproblem.get_fit_data()
875                    page = self.fit_panel.add_fit_page(data=data, reset=True)
876                    if fitproblem != None:
877                        self.page_finder[uid] = fitproblem
878                        if self.sim_page != None:
879                            self.sim_page.draw_page()
880                           
881                else:
882                    model = fitproblem
883                    self.fit_panel.add_model_page(model=model, topmenu=True,
884                                                  reset= True)
885                    break
886   
887    def _reset_schedule_problem(self, value=0, uid=None):
888        """
889        unschedule or schedule all fitproblem to be fit
890        """
891        # case that uid is not specified
892        if uid == None:
893            for page_id in self.page_finder.keys():
894                self.page_finder[page_id].schedule_tofit(value)
895        # when uid is given
896        else:
897            self.page_finder[uid].schedule_tofit(value)
898               
899    def _fit_setter(self, data, value, fitter, fit_id, pars):
900        """
901        """
902        model = value.get_model()
903        smearer = value.get_smearer()
904        qmin, qmax = value.get_range()
905        #Extra list of parameters and their constraints
906        listOfConstraint = []
907       
908        param = value.get_model_param()
909        if len(param) > 0:
910            for item in param:
911                ## check if constraint
912                if item[0] != None and item[1] != None:
913                    listOfConstraint.append((item[0],item[1]))
914               
915        #Do the single fit
916        fitter.set_model(deepcopy(model), fit_id,
917                               pars, constraints=listOfConstraint)
918       
919        fitter.set_data(data=data, id=fit_id,
920                             smearer=smearer, qmin=qmin, qmax=qmax)
921       
922        fitter.select_problem_for_fit(id=fit_id, value=1)
923        value.clear_model_param()
924           
925           
926    def _fit_helper(self, pars, value, fitproblem_id, fitter_list,
927                    fitter=None, title="Single Fit " ):
928        """
929        helper for fitting
930        """
931        from sans.fit.Fitting import Fit
932        self.fit_id = 0
933        data_list = value.get_fit_data_list()
934        pointer_to_fitproblem = value.get_pointer_to_fitproblem()
935        #Create list of parameters for fitting used
936        templist = []
937        for single_data in data_list:
938            fitter = Fit(self._fit_engine)
939            try:
940                if single_data.id in pointer_to_fitproblem:
941                     v = self.page_finder[pointer_to_fitproblem[single_data.id]]
942                     self._fit_setter(data=single_data, value=v, 
943                                     fitter=fitter,
944                                     pars=pars,
945                                      fit_id=self.fit_id)
946                else:
947                    self._fit_setter(data=single_data, value=value, 
948                                     fitter=fitter,
949                                     pars=pars,
950                                      fit_id=self.fit_id)
951            except:
952                raise
953                #msg = title + " error: %s" % sys.exc_value
954                #wx.PostEvent(self.parent, StatusEvent(status=msg, type="stop"))
955            fitter_list.append(fitter)
956            self.fit_id += 1
957           
958    def _onSelect(self,event):
959        """
960        when Select data to fit a new page is created .Its reference is
961        added to self.page_finder
962        """
963        self.panel = event.GetEventObject()
964        Plugin.on_perspective(self, event=event)
965        for plottable in self.panel.graph.plottables:
966            if plottable.__class__.__name__ in ["Data1D", "Theory1D"]:
967                data_id = self.panel.graph.selected_plottable
968                if plottable == self.panel.plots[data_id]:
969                    data = plottable
970                    self.add_fit_page(data=[data])
971                    return
972            else:
973                data = plottable
974                self.add_fit_page(data=[data])
975        self.set_top_panel()
976           
977    def update_fit(self, result=None, msg=""):
978        """
979        """
980        print "update_fit result", result
981       
982    def _batch_single_fit_complete_helper(self,result, pars, page_id, 
983                                          elapsed=None):
984        """
985        Fit result are display in batch mode
986        """
987        self._update_fit_button(page_id)
988        msg = "Single Fitting complete "
989        wx.PostEvent(self.parent, StatusEvent(status=msg, info="info",
990                                                      type="stop"))
991        if self.batch_on:
992            batch_result = {"Chi2":[]}
993            for index  in range(len(pars)):
994                    batch_result[pars[index]] = []
995                    batch_result["error on %s" % pars[index]] = []
996            for res in result:
997                batch_result["Chi2"].append(res.fitness)
998                for index  in range(len(pars)):
999                    batch_result[pars[index]].append(res.pvec[index])
1000                    item = res.stderr[index]
1001                    batch_result["error on %s" % pars[index]].append(item)
1002             
1003            pid = page_id[0]
1004            self.page_finder[pid].set_result(result=batch_result)     
1005            self.parent.on_set_batch_result(data=batch_result, 
1006                                            name=self.sub_menu)
1007           
1008       
1009    def _single_fit_completed(self, result, pars, page_id, elapsed=None):
1010        """
1011        Display fit result on one page of the notebook.
1012       
1013        :param result: result of fit
1014        :param pars: list of names of parameters fitted
1015        :param current_pg: the page where information will be displayed
1016        :param qmin: the minimum value of x to replot the model
1017        :param qmax: the maximum value of x to replot model
1018         
1019        """ 
1020        self._mac_sleep(0.2)
1021        if page_id[0] in self.fit_thread_list.keys():
1022            del self.fit_thread_list[page_id[0]] 
1023        if self.batch_on:
1024            wx.CallAfter(self._batch_single_fit_complete_helper,
1025                          result, pars, page_id, elapsed=None)
1026            return 
1027        else: 
1028            try:
1029                result = result[0]
1030                if result == None:
1031                    self._update_fit_button(page_id)
1032                    msg= "Single Fitting did not converge!!!"
1033                    wx.PostEvent(self.parent, 
1034                                 StatusEvent(status=msg, 
1035                                             info="warning",
1036                                             type="stop"))
1037                    return
1038                if not numpy.isfinite(result.fitness) or \
1039                        numpy.any(result.pvec == None) or \
1040                        not numpy.all(numpy.isfinite(result.pvec)):
1041                    msg = "Single Fitting did not converge!!!"
1042                    wx.PostEvent(self.parent, 
1043                                 StatusEvent(status=msg, 
1044                                             info="warning",
1045                                             type="stop"))
1046                    self._update_fit_button(page_id)
1047                    return
1048               
1049                for uid in page_id:
1050                    value = self.page_finder[uid]   
1051                    model = value.get_model()
1052                    page_id = uid
1053                       
1054                    param_name = []
1055                    for name in pars:
1056                        param_name.append(name)
1057       
1058                    cpage = self.fit_panel.get_page_by_id(uid)
1059                    # Make sure we got all results
1060                    #(CallAfter is important to MAC)
1061                    wx.CallAfter(cpage.onsetValues, result.fitness, 
1062                                      param_name, result.pvec, result.stderr)
1063                    cpage._on_fit_complete()
1064                if result.stderr == None:
1065                    msg = "Fit Abort: "
1066                else:
1067                    msg = "Fitting: "
1068                msg += "Completed!!!"
1069                wx.PostEvent(self.parent, StatusEvent(status=msg))
1070                return
1071            except ValueError:
1072                self._update_fit_button(page_id)
1073                msg = "Single Fitting did not converge!!!"
1074                wx.PostEvent(self.parent, StatusEvent(status=msg, info="error",
1075                                                      type="stop"))
1076                return 
1077            except:
1078                self._update_fit_button(page_id)
1079                msg = "Single Fit completed but Following"
1080                msg += " error occurred:%s" % sys.exc_value
1081                wx.PostEvent(self.parent, StatusEvent(status=msg, info="error",
1082                                                      type="stop"))
1083                raise
1084                return
1085           
1086    def _simul_fit_completed(self, result, page_id,pars=None, elapsed=None):
1087        """
1088        Parameter estimation completed,
1089        display the results to the user
1090       
1091        :param alpha: estimated best alpha
1092        :param elapsed: computation time
1093       
1094        """
1095        self.fit_thread_list = {}
1096        if page_id is None:
1097            page_id = []
1098        ## fit more than 1 model at the same time
1099        self._mac_sleep(0.2) 
1100        try:
1101            msg = "" 
1102            if result == None:
1103                self._update_fit_button(page_id)
1104                msg= "Complex Fitting did not converge!!!"
1105                wx.PostEvent(self.parent, StatusEvent(status=msg,
1106                                                      type="stop"))
1107                return
1108            if not numpy.isfinite(result.fitness) or \
1109                numpy.any(result.pvec == None) or not \
1110                numpy.all(numpy.isfinite(result.pvec)):
1111                self._update_fit_button(page_id)
1112                msg= "Simultaneous Fitting did not converge!!!"
1113                wx.PostEvent(self.parent, StatusEvent(status=msg,type="stop"))
1114                return
1115             
1116            for uid in page_id:   
1117                value = self.page_finder[uid]
1118                model = value.get_model()
1119                data =  value.get_fit_data()
1120                small_param_name = []
1121                small_out = []
1122                small_cov = []
1123                #Separate result in to data corresponding to each page
1124                for p in result.parameters:
1125                    model_name, param_name = self.split_string(p.name) 
1126                    if model.name == model_name:
1127                        p_name= model.name+"."+param_name
1128                        if p.name == p_name:     
1129                            if p.value != None and numpy.isfinite(p.value):
1130                                small_out.append(p.value)
1131                                small_param_name.append(param_name)
1132                                small_cov.append(p.stderr)
1133                # Display result on each page
1134                cpage = self.fit_panel.get_page_by_id(uid)
1135                wx.CallAfter(cpage.onsetValues, 
1136                                    result.fitness,
1137                                  small_param_name,
1138                                  small_out,small_cov)
1139                cpage._on_fit_complete()
1140                msg = "Fit completed!"
1141                wx.PostEvent(self.parent, StatusEvent(status=msg))
1142        except Exception:
1143            self._update_fit_button(page_id)
1144            msg = "Complex Fitting did not converge!!!"
1145            wx.PostEvent(self.parent, StatusEvent(status=msg, info="error",
1146                                                  type="stop"))
1147            return
1148
1149        except:
1150            self._update_fit_button(page_id)
1151            msg = "Simultaneous Fit completed"
1152            msg += " but Following error occurred:%s" % sys.exc_value
1153            wx.PostEvent(self.parent, StatusEvent(status=msg, type="stop"))
1154   
1155    def _update_fit_button(self, page_id):
1156        """
1157        Update Fit button when fit stopped
1158       
1159        : parameter page_id: fitpage where the button is
1160        """
1161        if page_id.__class__.__name__ != 'list':
1162            page_id = [page_id]
1163        for uid in page_id: 
1164            page = self.fit_panel.get_page_by_id(uid)
1165            page._on_fit_complete()
1166       
1167    def _on_show_panel(self, event):
1168        """
1169        """
1170        pass
1171       
1172    def _onset_engine_park(self,event):
1173        """
1174        set engine to park
1175        """
1176        self._on_change_engine('park')
1177       
1178    def _onset_engine_scipy(self,event):
1179        """
1180        set engine to scipy
1181        """
1182        self._on_change_engine('scipy')
1183       
1184    def _on_slicer_event(self, event):
1185        """
1186        Receive a panel as event and send it to guiframe
1187       
1188        :param event: event containing a panel
1189       
1190        """
1191        if event.panel is not None:
1192            new_panel = event.panel
1193            self.slicer_panels.append(event.panel)
1194            # Set group ID if available
1195            event_id = self.parent.popup_panel(new_panel)
1196            #self.menu3.Append(event_id, new_panel.window_caption,
1197            #                 "Show %s plot panel" % new_panel.window_caption)
1198            # Set id to allow us to reference the panel later
1199         
1200            new_panel.uid = event_id
1201            self.mypanels.append(new_panel) 
1202       
1203    def _onclearslicer(self, event):
1204        """
1205        Clear the boxslicer when close the panel associate with this slicer
1206        """
1207        name =event.GetPane().caption
1208   
1209        for panel in self.slicer_panels:
1210            if panel.window_caption==name:
1211               
1212                for item in self.parent.panels:
1213                    if hasattr(self.parent.panels[item],"uid"):
1214                        if self.parent.panels[item].uid ==panel.base.uid:
1215                            self.parent.panels[item].onClearSlicer(event)
1216                            self.parent._mgr.Update()
1217                            break 
1218                break
1219   
1220    def _return_engine_type(self):
1221        """
1222        return the current type of engine
1223        """
1224        return self._fit_engine
1225     
1226     
1227    def _on_change_engine(self, engine='park'):
1228        """
1229        Allow to select the type of engine to perform fit
1230       
1231        :param engine: the key work of the engine
1232       
1233        """
1234        ## saving fit engine name
1235        self._fit_engine = engine
1236        ## change menu item state
1237        if engine=="park":
1238            self.menu1.FindItemById(self.park_id).Check(True)
1239            self.menu1.FindItemById(self.scipy_id).Check(False)
1240        else:
1241            self.menu1.FindItemById(self.park_id).Check(False)
1242            self.menu1.FindItemById(self.scipy_id).Check(True)
1243        ## post a message to status bar
1244        msg = "Engine set to: %s" % self._fit_engine
1245        wx.PostEvent(self.parent, 
1246                     StatusEvent(status=msg))
1247        ## send the current engine type to fitpanel
1248        self.fit_panel._on_engine_change(name=self._fit_engine)
1249
1250       
1251    def _on_model_panel(self, evt):
1252        """
1253        react to model selection on any combo box or model menu.plot the model 
1254       
1255        :param evt: wx.combobox event
1256       
1257        """
1258        model = evt.model
1259        uid = evt.uid
1260        qmin = evt.qmin
1261        qmax = evt.qmax
1262        smearer = evt.smearer
1263       
1264        if model == None:
1265            return
1266       
1267        if self.page_finder[uid].get_model() is None:
1268            model.name = "M" + str(self.index_model)
1269            self.index_model += 1 
1270        else:
1271            model.name = self.page_finder[uid].get_model().name
1272        # save the name containing the data name with the appropriate model
1273        self.page_finder[uid].set_model(model)
1274        self.page_finder[uid].set_range(qmin=qmin, qmax=qmax)
1275        if self.sim_page is not None:
1276            self.sim_page.draw_page()
1277       
1278    def _update1D(self,x, output):
1279        """
1280        Update the output of plotting model 1D
1281        """
1282        msg = "Plot updating ... "
1283        wx.PostEvent(self.parent, StatusEvent(status=msg,type="update"))
1284        #self.ready_fit()
1285       
1286   
1287    def _fill_default_model2D(self, theory, page_id, qmax,qstep, qmin=None):
1288        """
1289        fill Data2D with default value
1290       
1291        :param theory: Data2D to fill
1292       
1293        """
1294        from DataLoader.data_info import Detector, Source
1295       
1296        detector = Detector()
1297        theory.detector.append(detector)         
1298        theory.source= Source()
1299       
1300        ## Default values   
1301        theory.detector[0].distance= 8000   # mm       
1302        theory.source.wavelength= 6         # A     
1303        theory.detector[0].pixel_size.x= 5  # mm
1304        theory.detector[0].pixel_size.y= 5  # mm
1305       
1306        theory.detector[0].beam_center.x= qmax
1307        theory.detector[0].beam_center.y= qmax
1308   
1309        ## create x_bins and y_bins of the model 2D
1310        pixel_width_x = theory.detector[0].pixel_size.x
1311        pixel_width_y = theory.detector[0].pixel_size.y
1312        center_x      = theory.detector[0].beam_center.x/pixel_width_x
1313        center_y      = theory.detector[0].beam_center.y/pixel_width_y
1314
1315        # theory default: assume the beam
1316        #center is located at the center of sqr detector
1317        xmax = qmax
1318        xmin = -qmax
1319        ymax = qmax
1320        ymin = -qmax
1321       
1322        x=  numpy.linspace(start= -1*qmax,
1323                               stop=qmax,
1324                               num=qstep,
1325                               endpoint=True) 
1326        y = numpy.linspace(start=-1*qmax,
1327                               stop= qmax,
1328                               num= qstep,
1329                               endpoint=True)
1330         
1331        ## use data info instead
1332        new_x = numpy.tile(x, (len(y),1))
1333        new_y = numpy.tile(y, (len(x),1))
1334        new_y = new_y.swapaxes(0,1)
1335       
1336        # all data reuire now in 1d array
1337        qx_data = new_x.flatten()
1338        qy_data = new_y.flatten()
1339       
1340        q_data = numpy.sqrt(qx_data*qx_data+qy_data*qy_data)
1341        # set all True (standing for unmasked) as default
1342        mask    = numpy.ones(len(qx_data), dtype = bool)
1343       
1344        # calculate the range of qx and qy: this way,
1345        # it is a little more independent
1346        x_size = xmax- xmin
1347        y_size = ymax -ymin
1348       
1349        # store x and y bin centers in q space
1350        x_bins  = x
1351        y_bins  = y
1352        # bin size: x- & y-directions
1353        xstep = x_size/len(x_bins-1)
1354        ystep = y_size/len(y_bins-1)
1355       
1356        #theory.data = numpy.zeros(len(mask))
1357        theory.err_data = numpy.ones(len(mask))
1358        theory.qx_data = qx_data
1359        theory.qy_data = qy_data 
1360        theory.q_data = q_data
1361        theory.mask = mask           
1362        theory.x_bins = x_bins 
1363        theory.y_bins = y_bins   
1364       
1365        # max and min taking account of the bin sizes
1366        theory.xmin = xmin
1367        theory.xmax = xmax
1368        theory.ymin = ymin
1369        theory.ymax = ymax
1370        theory.group_id = str(page_id) + " Model2D"
1371        theory.id = str(page_id) + " Model2D"
1372 
1373    def _complete1D(self, x,y, page_id, elapsed,index,model,
1374                    toggle_mode_on=False,state=None, 
1375                    data=None, update_chisqr=True):
1376        """
1377        Complete plotting 1D data
1378        """ 
1379        try:
1380            new_plot = Data1D(x=x, y=y)
1381            new_plot.is_data = False
1382            new_plot.symbol = GUIFRAME_ID.CURVE_SYMBOL_NUM
1383            if data != None:
1384                #if model.output_name.lower().count("reflectivity") > 0:
1385                #    _yaxis, _yunit = "\\rm{%s}"% model.output_name, \
1386                #                                "%s"% model.output_unit
1387                #else:
1388                _yaxis, _yunit = data.get_yaxis() 
1389                _xaxis, _xunit = data.get_xaxis() 
1390                new_plot.title = data.name
1391                #if the theory is already plotted use the same group id
1392                #to replot
1393                if page_id in self.page_finder:
1394                    theory_data = self.page_finder[page_id].get_theory_data()
1395                    if theory_data is not None:
1396                       data.group_id = theory_data.group_id
1397                #data is plotted before the theory, then take its group_id
1398                #assign to the new theory
1399                new_plot.group_id = data.group_id
1400               
1401            else:
1402                _xaxis, _xunit = "\\rm{%s}"% model.input_name, \
1403                                                "%s"% model.input_unit
1404                _yaxis, _yunit = "\\rm{%s}"% model.output_name, \
1405                                                "%s"% model.output_unit
1406                new_plot.title = "Analytical model 1D "
1407                #find a group id to plot theory without data
1408                new_plot.group_id =  str(page_id) + " Model1D" 
1409            new_plot.id =  str(page_id) + " Model1D" 
1410           
1411            #find if this theory was already plotted and replace that plot given
1412            #the same id
1413           
1414            theory_data = self.page_finder[page_id].get_theory_data()
1415            if theory_data is not None:
1416                new_plot.id = theory_data.id
1417             
1418            new_plot.name = model.name + " ["+ str(model.__class__.__name__)+ "]"
1419            new_plot.xaxis(_xaxis, _xunit)
1420            new_plot.yaxis(_yaxis, _yunit)
1421            if toggle_mode_on:
1422                new_plot.id =  str(page_id) + " Model" 
1423                wx.PostEvent(self.parent, 
1424                             NewPlotEvent(group_id=str(page_id) + " Model2D",
1425                                               action="Hide"))
1426           
1427            self.page_finder[page_id].set_theory_data(new_plot)
1428            if data is None:
1429                data_id = None
1430            else:
1431                data_id = data.id
1432
1433            self.parent.update_theory(data_id=data_id, 
1434                                       theory=new_plot,
1435                                       state=state)   
1436 
1437            current_pg = self.fit_panel.get_page_by_id(page_id)
1438            title = new_plot.title
1439
1440            wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot,
1441                                            title= str(title)))
1442
1443            self.page_finder[page_id].set_theory_data(new_plot)
1444
1445            if update_chisqr:
1446                wx.PostEvent(current_pg,
1447                             Chi2UpdateEvent(output=self._cal_chisqr(data=data,
1448                                                        page_id=page_id,
1449                                                        index=index)))
1450            else:
1451                self._plot_residuals(page_id, data, index)
1452
1453            msg = "Computation  completed!"
1454            wx.PostEvent( self.parent, StatusEvent(status=msg, type="stop" ))
1455
1456            #self.current_pg.state.theory_data = deepcopy(self.theory_data)
1457        except:
1458            raise
1459            #msg = " Error occurred when drawing %s Model 1D: " % new_plot.name
1460            #msg += " %s"  % sys.exc_value
1461            #wx.PostEvent(self.parent, StatusEvent(status=msg, type="stop"))
1462   
1463    def _update2D(self, output,time=None):
1464        """
1465        Update the output of plotting model
1466        """
1467        wx.PostEvent(self.parent, StatusEvent(status="Plot \
1468        #updating ... ", type="update"))
1469        #self.ready_fit()
1470 
1471    def _complete2D(self, image, data, model, page_id,  elapsed, index, qmin,
1472                     qmax, toggle_mode_on=False,state=None,qstep=DEFAULT_NPTS, 
1473                     update_chisqr=True):
1474        """
1475        Complete get the result of modelthread and create model 2D
1476        that can be plot.
1477        """
1478        err_image = numpy.zeros(numpy.shape(image))
1479       
1480        new_plot= Data2D(image=image, err_image=err_image)
1481        new_plot.name = model.name
1482        new_plot.title = "Analytical model 2D "
1483        if data is None:
1484            self._fill_default_model2D(theory=new_plot, 
1485                                       qmax=qmax, 
1486                                       page_id=page_id,
1487                                       qstep=qstep,
1488                                        qmin= qmin)
1489           
1490        else:
1491            new_plot.id = str(page_id) + " Model2D"
1492            new_plot.group_id = str(page_id) + " Model2D"
1493            new_plot.x_bins = data.x_bins
1494            new_plot.y_bins = data.y_bins
1495            new_plot.detector = data.detector
1496            new_plot.source = data.source
1497            new_plot.is_data = False 
1498            new_plot.qx_data = data.qx_data
1499            new_plot.qy_data = data.qy_data
1500            new_plot.q_data = data.q_data
1501            #numpy.zeros(len(data.err_data))#data.err_data
1502            new_plot.err_data = err_image
1503            new_plot.mask = data.mask
1504            ## plot boundaries
1505            new_plot.ymin = data.ymin
1506            new_plot.ymax = data.ymax
1507            new_plot.xmin = data.xmin
1508            new_plot.xmax = data.xmax
1509            title = data.title
1510            if len(title) > 1:
1511                new_plot.title = "Model2D for " + data.name
1512        new_plot.is_data = False
1513        new_plot.name = model.name + " ["+ str(model.__class__.__name__)+ "]"
1514
1515        theory_data = deepcopy(new_plot)
1516        theory_data.name = "Unknown"
1517        if toggle_mode_on:
1518            new_plot.id = str(page_id) + " Model"     
1519            wx.PostEvent(self.parent, 
1520                             NewPlotEvent(group_id=str(page_id) + " Model1D",
1521                                               action="Hide"))
1522       
1523        self.page_finder[page_id].set_theory_data(new_plot)
1524        if data is None:
1525            data_id = None
1526        else:
1527            data_id = data.id
1528        self.parent.update_theory(data_id=data_id, 
1529                                       theory=new_plot,
1530                                       state=state) 
1531        current_pg = self.fit_panel.get_page_by_id(page_id)
1532        title = new_plot.title
1533
1534        wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot,
1535                                               title=title))
1536        self.page_finder[page_id].set_theory_data(new_plot)
1537        # Chisqr in fitpage
1538        if update_chisqr:
1539            wx.PostEvent(current_pg,
1540                         Chi2UpdateEvent(output=\
1541                                    self._cal_chisqr(data=data,
1542                                                     page_id=page_id,
1543                                                     index=index)))
1544        else:
1545            self._plot_residuals(page_id, data, index)
1546        msg = "Computation  completed!"
1547        wx.PostEvent(self.parent, StatusEvent(status=msg, type="stop"))
1548   
1549    def _draw_model2D(self, model, page_id, data=None, smearer=None,
1550                      description=None, enable2D=False,
1551                      state=None,
1552                      toggle_mode_on=False,
1553                      qmin=DEFAULT_QMIN, qmax=DEFAULT_QMAX,
1554                      qstep=DEFAULT_NPTS,
1555                      update_chisqr=True):
1556        """
1557        draw model in 2D
1558       
1559        :param model: instance of the model to draw
1560        :param description: the description of the model
1561        :param enable2D: when True allows to draw model 2D
1562        :param qmin: the minimum value to  draw model 2D
1563        :param qmax: the maximum value to draw model 2D
1564        :param qstep: the number of division of Qx and Qy of the model to draw
1565           
1566        """
1567        x=  numpy.linspace(start=-1*qmax,
1568                               stop=qmax,
1569                               num=qstep,
1570                               endpoint=True) 
1571        y = numpy.linspace(start= -1*qmax,
1572                               stop=qmax,
1573                               num=qstep,
1574                               endpoint=True)
1575        if model is None:
1576            msg = "Panel with ID: %s does not contained model" % str(page_id)
1577            raise ValueError, msg
1578        ## use data info instead
1579        if data is not None:
1580            ## check if data2D to plot
1581            if hasattr(data, "x_bins"):
1582                enable2D = True
1583                x = data.x_bins
1584                y = data.y_bins
1585               
1586        if not enable2D:
1587            return None, None
1588        try:
1589            from model_thread import Calc2D
1590            ## If a thread is already started, stop it
1591            if (self.calc_2D is not None) and self.calc_2D.isrunning():
1592                self.calc_2D.stop()
1593            self.calc_2D = Calc2D(x=x,
1594                                    y=y,
1595                                    model=model, 
1596                                    data=data,
1597                                    page_id=page_id,
1598                                    smearer=smearer,
1599                                    qmin=qmin,
1600                                    qmax=qmax,
1601                                    qstep=qstep,
1602                                    toggle_mode_on=toggle_mode_on,
1603                                    state=state,
1604                                    completefn=self._complete2D,
1605                                    #updatefn= self._update2D,
1606                                    update_chisqr=update_chisqr)
1607
1608            self.calc_2D.queue()
1609
1610        except:
1611            raise
1612            #msg = " Error occurred when drawing %s Model 2D: " % model.name
1613            #msg += " %s" % sys.exc_value
1614            #wx.PostEvent(self.parent, StatusEvent(status=msg))
1615
1616    def _draw_model1D(self, model, page_id, data=None, smearer=None,
1617                qmin=DEFAULT_QMIN, qmax=DEFAULT_QMAX, 
1618                state=None,
1619                toggle_mode_on=False,
1620                qstep=DEFAULT_NPTS, update_chisqr=True, 
1621                enable1D=True):
1622        """
1623        Draw model 1D from loaded data1D
1624       
1625        :param data: loaded data
1626        :param model: the model to plot
1627       
1628        """
1629        x=  numpy.linspace(start=qmin,
1630                           stop=qmax,
1631                           num=qstep,
1632                           endpoint=True
1633                           )
1634        if data is not None:
1635            ## check for data2D
1636            if hasattr(data,"x_bins"):
1637                return
1638            x = data.x
1639            if qmin == None :
1640                qmin == DEFAULT_QMIN
1641
1642            if qmax == None:
1643                qmax == DEFAULT_QMAX
1644        if not enable1D:
1645            return 
1646        try:
1647            from model_thread import Calc1D
1648            ## If a thread is already started, stop it
1649            if (self.calc_1D is not None) and self.calc_1D.isrunning():
1650                self.calc_1D.stop()
1651            self.calc_1D = Calc1D(x=x,
1652                                  data=data,
1653                                  model=model,
1654                                  page_id=page_id, 
1655                                  qmin=qmin,
1656                                  qmax=qmax,
1657                                  smearer=smearer,
1658                                  state=state,
1659                                  toggle_mode_on=toggle_mode_on,
1660                                  completefn=self._complete1D,
1661                                  #updatefn = self._update1D,
1662                                  update_chisqr=update_chisqr)
1663
1664            self.calc_1D.queue()
1665        except:
1666            msg = " Error occurred when drawing %s Model 1D: " % model.name
1667            msg += " %s" % sys.exc_value
1668            wx.PostEvent(self.parent, StatusEvent(status=msg))
1669
1670    def _cal_chisqr(self, page_id, data=None, index=None): 
1671        """
1672        Get handy Chisqr using the output from draw1D and 2D,
1673        instead of calling expansive CalcChisqr in guithread
1674        """
1675        # default chisqr
1676        chisqr = None
1677
1678        # return None if data == None
1679        if data == None: return chisqr
1680       
1681        # Get data: data I, theory I, and data dI in order
1682        if data.__class__.__name__ == "Data2D":
1683            if index == None: 
1684                index = numpy.ones(len(data.data),ntype=bool)
1685            # get rid of zero error points
1686            index = index & (data.err_data != 0 ) 
1687            index = index & (numpy.isfinite(data.data)) 
1688            fn = data.data[index] 
1689            theory_data = self.page_finder[page_id].get_theory_data()
1690            gn = theory_data.data[index]
1691            en = data.err_data[index]
1692        else:
1693            # 1 d theory from model_thread is only in the range of index
1694            if index == None: 
1695                index = numpy.ones(len(data.y), ntype=bool)
1696            if data.dy == None or data.dy == []:
1697                dy = numpy.ones(len(data.y))
1698            else:
1699                ## Set consitently w/AbstractFitengine:
1700                # But this should be corrected later.
1701                dy = deepcopy(data.dy)
1702                dy[dy==0] = 1 
1703            fn = data.y[index] 
1704            theory_data = self.page_finder[page_id].get_theory_data()
1705            gn = theory_data.y
1706            en = dy[index]
1707
1708        # residual
1709        res = (fn - gn) / en
1710        residuals = res[numpy.isfinite(res)]
1711        # get chisqr only w/finite
1712        chisqr = numpy.average(residuals * residuals)
1713       
1714        self._plot_residuals(page_id, data, index)
1715        return chisqr
1716   
1717
1718       
1719    def _plot_residuals(self, page_id, data=None, index=None): 
1720        """
1721        Plot the residuals
1722       
1723        :param data: data
1724        :param index: index array (bool)
1725        : Note: this is different from the residuals in cal_chisqr()
1726        """
1727        if data == None: 
1728            return 
1729       
1730        # Get data: data I, theory I, and data dI in order
1731        if data.__class__.__name__ == "Data2D":
1732            # build residuals
1733            #print data
1734            residuals = Data2D()
1735            #residuals.copy_from_datainfo(data)
1736            # Not for trunk the line below, instead use the line above
1737            data.clone_without_data(len(data.data), residuals)
1738            residuals.data = None
1739            fn = data.data#[index]
1740            theory_data = self.page_finder[page_id].get_theory_data()
1741            gn = theory_data.data#[index]
1742            en = data.err_data#[index]
1743            residuals.data = (fn - gn) / en
1744            residuals.qx_data = data.qx_data#[index]
1745            residuals.qy_data = data.qy_data #[index]
1746            residuals.q_data = data.q_data#[index]
1747            residuals.err_data = numpy.ones(len(residuals.data))#[index]
1748            residuals.xmin = min(residuals.qx_data)
1749            residuals.xmax = max(residuals.qx_data)
1750            residuals.ymin = min(residuals.qy_data)
1751            residuals.ymax = max(residuals.qy_data)
1752            residuals.q_data = data.q_data#[index]
1753            residuals.mask = data.mask
1754            residuals.scale = 'linear'
1755            #print "print data",residuals
1756            # check the lengths
1757            if len(residuals.data) != len(residuals.q_data):
1758                return
1759
1760        else:
1761            # 1 d theory from model_thread is only in the range of index
1762            if data.dy == None or data.dy == []:
1763                dy = numpy.ones(len(data.y))
1764            else:
1765                ## Set consitently w/AbstractFitengine:
1766                ## But this should be corrected later.
1767                dy = deepcopy(data.dy)
1768                dy[dy==0] = 1 
1769            fn = data.y[index] 
1770            theory_data = self.page_finder[page_id].get_theory_data()
1771            gn = theory_data.y
1772            en = dy[index]
1773            # build residuals
1774            residuals = Data1D()
1775            residuals.y = (fn - gn) / en
1776            residuals.x = data.x[index]
1777            residuals.dy = numpy.ones(len(residuals.y))
1778            residuals.dx = None
1779            residuals.dxl = None
1780            residuals.dxw = None
1781            residuals.ytransform = 'y'
1782            # For latter scale changes
1783            residuals.xaxis('\\rm{Q} ', 'A^{-1}')
1784            residuals.yaxis('\\rm{Residuals} ', 'normalized')
1785           
1786        new_plot = residuals
1787        if data.id == None:
1788            data.id = data.name
1789        name  = data.id
1790        new_plot.name = "Residuals for " + str(data.name)
1791        ## allow to highlight data when plotted
1792        new_plot.interactive = True
1793        ## when 2 data have the same id override the 1 st plotted
1794        new_plot.id = new_plot.name#name + " residuals"
1795        ##group_id specify on which panel to plot this data
1796        new_plot.group_id = new_plot.id
1797        #new_plot.is_data = True
1798        ##post data to plot
1799        title = new_plot.name
1800       
1801        # plot data
1802        wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot, title=title))   
1803       
1804#def profile(fn, *args, **kw):
1805#    import cProfile, pstats, os
1806#    global call_result
1807#    def call():
1808#        global call_result
1809#        call_result = fn(*args, **kw)
1810#    cProfile.runctx('call()', dict(call=call), {}, 'profile.out')
1811#    stats = pstats.Stats('profile.out')
1812#    #stats.sort_stats('time')
1813#    stats.sort_stats('calls')
1814#    stats.print_stats()
1815#    os.unlink('profile.out')
1816#    return call_result
1817if __name__ == "__main__":
1818    i = Plugin()
1819   
1820   
1821   
1822   
Note: See TracBrowser for help on using the repository browser.