source: sasview/fittingview/src/sans/perspectives/fitting/fitting.py @ 06e0636

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

another trial

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