source: sasview/fittingview/src/sans/perspectives/fitting/fitting.py @ 9c1af44

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

change comment

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