source: sasview/fittingview/src/sans/perspectives/fitting/fitting.py @ 3d2d54a6

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 3d2d54a6 was 9f4f8f4, checked in by Jessica Tumarkin <jtumarki@…>, 13 years ago

committing custom_color stuff that I did not commit earlier

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