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

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

make sure residuals are proprely stored after the fit

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