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

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

make sure all model parameters are displayed in the grid whether or not they were fitted

  • Property mode set to 100644
File size: 77.6 KB
Line 
1
2
3################################################################################
4#This software was developed by the University of Tennessee as part of the
5#Distributed Data Analysis of Neutron Scattering Experiments (DANSE)
6#project funded by the US National Science Foundation.
7#
8#See the license text in license.txt
9#
10#copyright 2009, University of Tennessee
11################################################################################
12
13
14import re
15import sys
16import wx
17import logging
18import numpy
19import string
20import time
21from copy import deepcopy
22import models
23import fitpage
24
25
26from sans.dataloader.loader import Loader
27from sans.guiframe.dataFitting import Data2D
28from sans.guiframe.dataFitting import Data1D
29from sans.guiframe.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):
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        else:     
679            ## draw model 2D with no initial data
680            self._draw_model2D(model=model,
681                                page_id=page_id,
682                                data=data,
683                                enable2D=enable2D,
684                                smearer=smearer,
685                                qmin=qmin,
686                                qmax=qmax,
687                                fid=fid,
688                                weight=weight,
689                                state=state,
690                                toggle_mode_on=toggle_mode_on,
691                                update_chisqr=update_chisqr)
692           
693    def onFit(self, uid):
694        """
695        Get series of data, model, associates parameters and range and send then
696        to  series of fit engines. Fit data and model, display result to
697        corresponding panels.
698        :param uid: id related to the panel currently calling this fit function.
699        """
700        flag = True
701        ##  count the number of fitproblem schedule to fit
702        fitproblem_count = 0
703        for value in self.page_finder.values():
704            if value.get_scheduled() == 1:
705                fitproblem_count += 1
706               
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        is_single_fit = True
715        if self.sim_page is not None and self.sim_page.uid == uid:
716            #simulatanous fit only one engine need to be created
717            ## if simultaneous fit change automatically the engine to park
718            self._on_change_engine(engine='park')   
719            sim_fitter = Fit(self._fit_engine) 
720            fitter_list.append(sim_fitter) 
721            is_single_fit = False
722
723        self.fitproblem_count = fitproblem_count 
724        if self._fit_engine == "park":
725            engineType = "Simultaneous Fit"
726        else:
727            engineType = "Single Fit"
728       
729        self.current_pg = None
730        list_page_id = []
731        fit_id = 0
732        batch_inputs = {}
733        batch_outputs = {}
734        for page_id, value in self.page_finder.iteritems():
735            # For simulfit (uid give with None), do for-loop
736            # if uid is specified (singlefit), do it only on the page.
737            if engineType == "Single Fit":
738                if page_id != uid:
739                    continue
740            try:
741                if value.get_scheduled() == 1:
742                    value.nbr_residuals_computed = 0
743                    #Get list of parameters name to fit
744                    pars = []
745                    templist = []
746                    page = self.fit_panel.get_page_by_id(page_id)
747                    self.set_fit_weight(uid=page.uid, 
748                                     flag=page.get_weight_flag(),
749                                     is2d = page._is_2D())
750                    templist = page.get_param_list()
751                    flag = page._update_paramv_on_fit() 
752                    if not flag:
753                        msg = "Fitting range or parameter values are"
754                        msg += " invalid in %s"% \
755                                    page.window_caption
756                        wx.PostEvent(page.parent.parent, 
757                                     StatusEvent(status= msg, info="error",
758                                     type="stop"))
759                        return flag
760                    for element in templist:
761                        name = str(element[1])
762                        pars.append(name)
763                    fitproblem_list = value.values()
764                    for fitproblem in  fitproblem_list:
765                        if sim_fitter is None:
766                            fitter = Fit(self._fit_engine) 
767                            self._fit_helper(fitproblem=fitproblem, 
768                                                pars=pars, 
769                                                fitter=fitter,
770                                              fit_id=fit_id, 
771                                              batch_inputs=batch_inputs,
772                                              batch_outputs=batch_outputs)
773                            fitter_list.append(fitter) 
774                        else:
775                            fitter = sim_fitter
776                            self._fit_helper(fitproblem=fitproblem, 
777                                                pars=pars, 
778                                                fitter=fitter,
779                                              fit_id=fit_id, 
780                                              batch_inputs=batch_inputs,
781                                              batch_outputs=batch_outputs)
782                        fit_id += 1
783                    list_page_id.append(page_id)
784                    current_page_id = page_id
785                    value.clear_model_param()
786            except:
787                flag = False
788                msg= "%s error: %s" % (engineType, sys.exc_value)
789                wx.PostEvent(self.parent, StatusEvent(status=msg, info="error",
790                                                      type="stop"))
791                return flag
792        ## If a thread is already started, stop it
793        #if self.calc_fit!= None and self.calc_fit.isrunning():
794        #    self.calc_fit.stop()
795        msg = "Fitting is in progress..."
796        wx.PostEvent( self.parent, StatusEvent(status=msg, type="progress" ))
797       
798        #Handler used for park engine displayed message
799        handler = ConsoleUpdate(parent=self.parent,
800                                manager=self,
801                                improvement_delta=0.1)
802        self._mac_sleep(0.2)
803        ## perform single fit
804        if is_single_fit:
805            calc_fit = FitThread(handler = handler,
806                                    fn=fitter_list,
807                                    pars=pars,
808                                    batch_inputs=batch_inputs,
809                                    batch_outputs=batch_outputs,
810                                    page_id=list_page_id,
811                                    completefn=self._single_fit_completed,
812                                    ftol=self.ftol)
813        else:
814            current_page_id = self.sim_page.uid
815            ## Perform more than 1 fit at the time
816            calc_fit = FitThread(handler=handler,
817                                    fn=fitter_list,
818                                    batch_inputs=batch_inputs,
819                                    batch_outputs=batch_outputs,
820                                    page_id=list_page_id,
821                                    updatefn=handler.update_fit,
822                                    completefn=self._simul_fit_completed,
823                                    ftol=self.ftol)
824        self.fit_thread_list[current_page_id] = calc_fit
825        calc_fit.queue()
826        msg = "Fitting is in progress..."
827        wx.PostEvent( self.parent, StatusEvent(status=msg, type="progress" ))
828       
829        self.ready_fit(calc_fit=calc_fit)
830        return flag
831   
832    def ready_fit(self, calc_fit):
833        """
834        Ready for another fit
835        """
836        if self.fitproblem_count != None and self.fitproblem_count > 1:
837            calc_fit.ready(2.5)
838        else:
839            time.sleep(0.4)
840           
841    def remove_plot(self, uid, fid=None, theory=False):
842        """
843        remove model plot when a fit page is closed
844        :param uid: the id related to the fitpage to close
845        :param fid: the id of the fitproblem(data, model, range,etc)
846        """
847        if uid not in self.page_finder.keys():
848            return
849        fitproblemList = self.page_finder[uid].get_fit_problem(fid)
850        for fitproblem in fitproblemList:
851            data = fitproblem.get_fit_data()
852            model = fitproblem.get_model()
853            plot_id = None
854            if model is not None:
855                plot_id = data.id + name
856            if theory:
857                plot_id = data.id
858            group_id = data.group_id
859            wx.PostEvent(self.parent, NewPlotEvent(id=plot_id,
860                                                       group_id=group_id,
861                                                       action='remove'))
862           
863    def store_data(self, uid, data_list=None, caption=None):
864        """
865        Recieve a list of data and store them ans well as a caption of
866        the fit page where they come from.
867        :param uid: if related to a fit page
868        :param data_list: list of data to fit
869        :param caption: caption of the window related to these data
870        """
871        if data_list is None:
872            data_list = []
873       
874        self.page_finder[uid].set_fit_data(data=data_list)
875        if caption is not None:
876            self.page_finder[uid].set_fit_tab_caption(caption=caption)
877           
878    def on_add_new_page(self, event=None):
879        """
880        ask fit panel to create a new empty page
881        """
882        try:
883            page = self.fit_panel.add_empty_page()
884            page_caption = page.window_caption
885            # add data associated to the page created
886            if page != None: 
887                wx.PostEvent(self.parent, StatusEvent(status="Page Created",
888                                               info="info"))
889            else:
890                msg = "Page was already Created"
891                wx.PostEvent(self.parent, StatusEvent(status=msg,
892                                                       info="warning"))
893            self.set_top_panel()
894        except:
895            msg = "Creating Fit page: %s"%sys.exc_value
896            wx.PostEvent(self.parent, StatusEvent(status=msg, info="error"))
897       
898    def add_fit_page(self, data):
899        """
900        given a data, ask to the fitting panel to create a new fitting page,
901        get this page and store it into the page_finder of this plug-in
902        :param data: is a list of data
903        """
904        page = self.fit_panel.set_data(data)
905        # page could be None when loading state files
906        if page == None:
907            return page
908        page_caption = page.window_caption
909        #append Data1D to the panel containing its theory
910        #if theory already plotted
911        if page.uid in self.page_finder:
912            data = page.get_data()
913            theory_data = self.page_finder[page.uid].get_theory_data(data.id)
914            if issubclass(data.__class__, Data2D):
915                data.group_id = wx.NewId()
916                if theory_data is not None:
917                    group_id = str(page.uid) + " Model1D"
918                    wx.PostEvent(self.parent, 
919                             NewPlotEvent(group_id=group_id,
920                                               action="delete"))
921                    self.parent.update_data(prev_data=theory_data,
922                                             new_data=data)     
923            else:
924                if theory_data is not None:
925                    group_id = str(page.uid) + " Model2D"
926                    data.group_id = theory_data.group_id
927                    wx.PostEvent(self.parent, 
928                             NewPlotEvent(group_id=group_id,
929                                               action="delete"))
930                    self.parent.update_data(prev_data=theory_data,
931                                             new_data=data)   
932        self.store_data(uid=page.uid, data_list=page.get_data_list(), 
933                        caption=page.window_caption)
934        if self.sim_page is not None and not self.batch_on:
935            self.sim_page.draw_page()
936        return page
937           
938    def _onEVT_SLICER_PANEL(self, event):
939        """
940        receive and event telling to update a panel with a name starting with
941        event.panel_name. this method update slicer panel
942        for a given interactor.
943       
944        :param event: contains type of slicer , paramaters for updating
945            the panel and panel_name to find the slicer 's panel concerned.
946        """
947        for item in self.parent.panels:
948            name = event.panel_name
949            if self.parent.panels[item].window_caption.startswith(name):
950                self.parent.panels[item].set_slicer(event.type, event.params)
951               
952        self.parent._mgr.Update()
953   
954    def _closed_fitpage(self, event):   
955        """
956        request fitpanel to close a given page when its unique data is removed
957        from the plot. close fitpage only when the a loaded data is removed
958        """   
959        if event is None or event.data is None:
960            return
961        if hasattr(event.data,"is_data"):
962            if not event.data.is_data or \
963                event.data.__class__.__name__ == "Data1D":
964                self.fit_panel.close_page_with_data(event.data) 
965 
966    def _reset_schedule_problem(self, value=0, uid=None):
967        """
968        unschedule or schedule all fitproblem to be fit
969        """
970        # case that uid is not specified
971        if uid == None:
972            for page_id in self.page_finder.keys():
973                self.page_finder[page_id].schedule_tofit(value)
974        # when uid is given
975        else:
976            if uid in self.page_finder.keys():
977                self.page_finder[uid].schedule_tofit(value)
978               
979    def _fit_helper(self, fitproblem, pars, fitter, fit_id,
980                    batch_inputs, batch_outputs):
981        """
982        Create and set fit engine with series of data and model
983        :param pars: list of fittable parameters
984        :param fitter_list: list of fit engine
985        :param value:  structure storing data mapped to their model, range etc..
986        """
987        data = fitproblem.get_fit_data()
988        model = fitproblem.get_model()
989        smearer = fitproblem.get_smearer()
990        qmin, qmax = fitproblem.get_range()
991
992        #Extra list of parameters and their constraints
993        listOfConstraint = []
994        param = fitproblem.get_model_param()
995        if len(param) > 0:
996            for item in param:
997                ## check if constraint
998                if item[0] != None and item[1] != None:
999                    listOfConstraint.append((item[0],item[1]))
1000        new_model = model#deepcopy(model)
1001        fitter.set_model(new_model, fit_id, pars, data=data,
1002                         constraints=listOfConstraint)
1003        fitter.set_data(data=data, id=fit_id, smearer=smearer, qmin=qmin, 
1004                        qmax=qmax)
1005        fitter.select_problem_for_fit(id=fit_id, value=1)
1006       
1007   
1008    def _onSelect(self,event):
1009        """
1010        when Select data to fit a new page is created .Its reference is
1011        added to self.page_finder
1012        """
1013        self.panel = event.GetEventObject()
1014        Plugin.on_perspective(self, event=event)
1015        self.select_data(self.panel)
1016       
1017    def select_data(self, panel):
1018        """
1019        """
1020        self.panel = panel
1021        for plottable in self.panel.graph.plottables:
1022            if plottable.__class__.__name__ in ["Data1D", "Theory1D"]:
1023                data_id = self.panel.graph.selected_plottable
1024                if plottable == self.panel.plots[data_id]:
1025                    data = plottable
1026                    self.add_fit_page(data=[data])
1027                    return
1028            else:
1029                data = plottable
1030                self.add_fit_page(data=[data])
1031        self.set_top_panel()
1032           
1033    def update_fit(self, result=None, msg=""):
1034        """
1035        """
1036        print "update_fit result", result
1037       
1038   
1039       
1040    def _batch_single_fit_complete_helper(self, result, pars, page_id, 
1041                            batch_outputs, batch_inputs, elapsed=None):
1042        """
1043        Display fit result in batch
1044        :param result: list of objects received fromt fit engines
1045        :param pars: list of  fitted parameters names
1046        :param page_id: list of page ids which called fit function
1047        :param elapsed: time spent at the fitting level
1048        """
1049        self._update_fit_button(page_id)
1050        msg = "Single Fitting complete "
1051        wx.PostEvent(self.parent, StatusEvent(status=msg, info="info",
1052                                                      type="stop"))
1053        pid = page_id[0]
1054        if batch_outputs is None:
1055            batch_outputs = {}
1056        if self.batch_on:
1057            # format batch_outputs
1058            batch_outputs["Chi2"] = []
1059            for index  in range(len(pars)):
1060                batch_outputs[pars[index]] = []
1061                batch_inputs["error on %s" % pars[index]] = []
1062            for res in result:
1063                model, data = res.inputs[0]
1064                temp_model = model#.clone()
1065                if res is None:
1066                    null_value = numpy.nan
1067                    cell = BatchCell()
1068                    cell.label = null_value
1069                    cell.value = null_value
1070                    cell.object = [None]
1071                    batch_outputs["Chi2"].append(cell)
1072                    for param in temp_model.getParamList():
1073                        if temp_model.is_fittable(param):
1074                            for index  in range(len(pars)):
1075                                #replug only fitted values
1076                                if param != pars[index]:
1077                                    batch_outputs[pars[index]].append(null_value)
1078                                    item = null_value
1079                                    batch_inputs["error on %s" % pars[index]].append(item)
1080                                    if pars[index] in model.getParamList():
1081                                        temp_model.setParam(pars[index], res.pvec[index])
1082                                else:
1083                                     batch_outputs[str(param)].append(temp_model.getParam(param))
1084                else:
1085                    from sans.guiframe.data_processor import BatchCell
1086                    cell = BatchCell()
1087                    cell.label = res.fitness
1088                    cell.value = res.fitness
1089                    batch_outputs["Chi2"].append(cell)
1090                    for param in temp_model.getParamList():
1091                        if temp_model.is_fittable(param):
1092                            for index  in range(len(pars)):
1093                                #replug only fitted values
1094                                if param != pars[index]:
1095                                    batch_outputs[pars[index]].append(res.pvec[index])
1096                                    item = res.stderr[index]
1097                                    batch_inputs["error on %s" % pars[index]].append(item)
1098                                    if pars[index] in temp_model.getParamList():
1099                                        temp_model.setParam(pars[index], res.pvec[index])
1100                                else:
1101                                     batch_outputs[str(param)].append(temp_model.getParam(param))
1102                               
1103                self.page_finder[pid].set_batch_result(batch_inputs=batch_inputs,
1104                                                 batch_outputs=batch_outputs)   
1105                cpage = self.fit_panel.get_page_by_id(pid)
1106                cpage._on_fit_complete()
1107                self.page_finder[pid][data.id].set_result(res)
1108                fitproblem = self.page_finder[pid][data.id]
1109               
1110               
1111                from sans.models.qsmearing import smear_selection
1112                #smearer = fitproblem.get_smearer()
1113                smearer = smear_selection(data, temp_model)
1114                qmin, qmax = fitproblem.get_range()
1115                weight = fitproblem.get_weight()
1116                flag = issubclass(data.__class__, Data2D)
1117                self.draw_model(model=temp_model,
1118                                  page_id=pid, 
1119                                  data=data, 
1120                                  smearer=smearer,
1121                                  enable1D=not flag, 
1122                                  enable2D=flag,
1123                                  state=None,
1124                                  toggle_mode_on=False,
1125                                  fid=data.id,
1126                                  qmin=qmin, qmax=qmax, 
1127                                  update_chisqr=False, 
1128                                  weight=weight)
1129       
1130    def on_set_batch_result(self, page_id, fid, batch_outputs, batch_inputs):
1131        """
1132        """
1133       
1134        pid =  page_id
1135        if fid not in self.page_finder[pid]:
1136            return
1137        fitproblem = self.page_finder[pid][fid]
1138        index = self.page_finder[pid].nbr_residuals_computed - 1
1139        residuals =  fitproblem.get_residuals()
1140        theory_data = fitproblem.get_theory_data()
1141        data = fitproblem.get_fit_data()
1142        model = fitproblem.get_model()
1143        #fill batch result information
1144        if "Data" not in batch_outputs.keys():
1145            batch_outputs["Data"] = []
1146        from sans.guiframe.data_processor import BatchCell
1147        cell = BatchCell()
1148        cell.label = data.name
1149        cell.value = index
1150        theory_data.id = wx.NewId()
1151        theory_data.name = data.name + "[%s]" % str(model.__class__.__name__)
1152        cell.object = [data, theory_data]
1153        batch_outputs["Data"].append(cell)
1154        for key, value in data.meta_data.iteritems():
1155            if key not in batch_inputs.keys():
1156                batch_inputs[key] = []
1157            if key.lower().strip() != "loader":
1158                batch_inputs[key].append(value)
1159        param = "temperature"
1160        if hasattr(data.sample, param):
1161            if param not in  batch_inputs.keys():
1162                 batch_inputs[param] = []
1163            batch_inputs[param].append(data.sample.temperature)
1164        # associate residuals plot
1165        batch_outputs["Chi2"][index].object = [residuals]
1166       
1167    def _single_fit_completed(self, result, pars, page_id, batch_outputs,
1168                          batch_inputs=None,  elapsed=None):
1169        """
1170         Display fit result on one page of the notebook.
1171        :param result: list of object generated when fit ends
1172        :param pars: list of names of parameters fitted
1173        :param page_id: list of page ids which called fit function
1174        :param elapsed: time spent at the fitting level
1175        """ 
1176        self._mac_sleep(0.2)
1177        uid = page_id[0]
1178        if uid in self.fit_thread_list.keys():
1179            del self.fit_thread_list[uid] 
1180         
1181        cpage = self.fit_panel.get_page_by_id(uid)
1182        if cpage.batch_on:
1183            wx.CallAfter(self._batch_single_fit_complete_helper,
1184                          result, pars, page_id, batch_outputs, 
1185                          batch_inputs, elapsed)
1186            return 
1187        else: 
1188            try:
1189                result = result[0]
1190                if result == None:
1191                    self._update_fit_button(page_id)
1192                    msg= "Single Fitting did not converge!!!"
1193                    wx.PostEvent(self.parent, 
1194                                 StatusEvent(status=msg, 
1195                                             info="warning",
1196                                             type="stop"))
1197                    return
1198                if not numpy.isfinite(result.fitness) or \
1199                        numpy.any(result.pvec == None) or \
1200                        not numpy.all(numpy.isfinite(result.pvec)):
1201                    msg = "Single Fitting did not converge!!!"
1202                    wx.PostEvent(self.parent, 
1203                                 StatusEvent(status=msg, 
1204                                             info="warning",
1205                                             type="stop"))
1206                    self._update_fit_button(page_id)
1207                    return
1208               
1209                for uid in page_id:
1210                    cpage = self.fit_panel.get_page_by_id(uid)
1211                    # Make sure we got all results
1212                    #(CallAfter is important to MAC)
1213                    wx.CallAfter(cpage.onsetValues, result.fitness, pars, 
1214                                 result.pvec, result.stderr)
1215                    cpage._on_fit_complete()
1216                if result.stderr == None:
1217                    msg = "Fit Abort: "
1218                else:
1219                    msg = "Fitting: "
1220                msg += "Completed!!!"
1221                wx.PostEvent(self.parent, StatusEvent(status=msg))
1222            except ValueError:
1223                raise
1224                self._update_fit_button(page_id)
1225                msg = "Single Fitting did not converge!!!"
1226                wx.PostEvent(self.parent, StatusEvent(status=msg, info="error",
1227                                                      type="stop"))
1228            except:
1229                raise
1230                self._update_fit_button(page_id)
1231                msg = "Single Fit completed but Following"
1232                msg += " error occurred:%s" % sys.exc_value
1233                wx.PostEvent(self.parent, StatusEvent(status=msg, info="error",
1234                                                      type="stop"))
1235                raise
1236               
1237    def _simul_fit_completed(self, result, page_id, batch_outputs,
1238                             batch_inputs=None,
1239                              pars=None, 
1240                             elapsed=None):
1241        """
1242        Display result of the fit on related panel(s).
1243        :param result: list of object generated when fit ends
1244        :param pars: list of names of parameters fitted
1245        :param page_id: list of page ids which called fit function
1246        :param elapsed: time spent at the fitting level
1247        """
1248        result = result[0]
1249        self.fit_thread_list = {}
1250        if page_id is None:
1251            page_id = []
1252        ## fit more than 1 model at the same time
1253        self._mac_sleep(0.2) 
1254        try:
1255            msg = "" 
1256            if result == None:
1257                self._update_fit_button(page_id)
1258                msg= "Complex Fitting did not converge!!!"
1259                wx.PostEvent(self.parent, StatusEvent(status=msg,
1260                                                      type="stop"))
1261                return
1262            if not numpy.isfinite(result.fitness) or \
1263                numpy.any(result.pvec == None) or not \
1264                numpy.all(numpy.isfinite(result.pvec)):
1265                self._update_fit_button(page_id)
1266                msg= "Simultaneous Fitting did not converge!!!"
1267                wx.PostEvent(self.parent, StatusEvent(status=msg,type="stop"))
1268                return
1269             
1270            for uid in page_id:   
1271                fpdict = self.page_finder[uid]
1272                for value in fpdict.itervalues():
1273                    model = value.get_model()
1274                    data =  value.get_fit_data()
1275                    small_param_name = []
1276                    small_out = []
1277                    small_cov = []
1278                    #Separate result in to data corresponding to each page
1279                    for p in result.parameters:
1280                        #print "simul ----", p , p.__class__, p.model.name, p.data.name
1281                        model_name, param_name = self.split_string(p.name) 
1282                        if model.name == model_name:
1283                            p_name= model.name+"."+param_name
1284                            if p.name == p_name:     
1285                                if p.value != None and numpy.isfinite(p.value):
1286                                    small_out.append(p.value)
1287                                    small_param_name.append(param_name)
1288                                    small_cov.append(p.stderr)
1289                # Display result on each page
1290                cpage = self.fit_panel.get_page_by_id(uid)
1291                wx.CallAfter(cpage.onsetValues, 
1292                                    result.fitness,
1293                                  small_param_name,
1294                                  small_out,small_cov)
1295                cpage._on_fit_complete()
1296                msg = "Fit completed!"
1297                wx.PostEvent(self.parent, StatusEvent(status=msg))
1298        except Exception:
1299            raise
1300            self._update_fit_button(page_id)
1301            msg = "Complex Fitting did not converge!!!"
1302            wx.PostEvent(self.parent, StatusEvent(status=msg, info="error",
1303                                                  type="stop"))
1304            return
1305        except:
1306            self._update_fit_button(page_id)
1307            msg = "Simultaneous Fit completed"
1308            msg += " but Following error occurred:%s" % sys.exc_value
1309            wx.PostEvent(self.parent, StatusEvent(status=msg, type="stop"))
1310   
1311    def _update_fit_button(self, page_id):
1312        """
1313        Update Fit button when fit stopped
1314       
1315        : parameter page_id: fitpage where the button is
1316        """
1317        if page_id.__class__.__name__ != 'list':
1318            page_id = [page_id]
1319        for uid in page_id: 
1320            page = self.fit_panel.get_page_by_id(uid)
1321            page._on_fit_complete()
1322       
1323    def _on_show_panel(self, event):
1324        """
1325        """
1326        pass
1327       
1328    def _onset_engine_park(self,event):
1329        """
1330        set engine to park
1331        """
1332        self._on_change_engine('park')
1333       
1334    def _onset_engine_scipy(self,event):
1335        """
1336        set engine to scipy
1337        """
1338        self._on_change_engine('scipy')
1339       
1340    def _on_slicer_event(self, event):
1341        """
1342        Receive a panel as event and send it to guiframe
1343       
1344        :param event: event containing a panel
1345       
1346        """
1347        if event.panel is not None:
1348            new_panel = event.panel
1349            self.slicer_panels.append(event.panel)
1350            # Set group ID if available
1351            event_id = self.parent.popup_panel(new_panel)
1352            new_panel.uid = event_id
1353            self.mypanels.append(new_panel) 
1354       
1355    def _onclearslicer(self, event):
1356        """
1357        Clear the boxslicer when close the panel associate with this slicer
1358        """
1359        name =event.GetPane().caption
1360   
1361        for panel in self.slicer_panels:
1362            if panel.window_caption==name:
1363               
1364                for item in self.parent.panels:
1365                    if hasattr(self.parent.panels[item], "uid"):
1366                        if self.parent.panels[item].uid ==panel.base.uid:
1367                            self.parent.panels[item].onClearSlicer(event)
1368                            self.parent._mgr.Update()
1369                            break 
1370                break
1371   
1372    def _return_engine_type(self):
1373        """
1374        return the current type of engine
1375        """
1376        return self._fit_engine
1377     
1378     
1379    def _on_change_engine(self, engine='park'):
1380        """
1381        Allow to select the type of engine to perform fit
1382       
1383        :param engine: the key work of the engine
1384       
1385        """
1386        ## saving fit engine name
1387        self._fit_engine = engine
1388        ## change menu item state
1389        if engine == "park":
1390            self.menu1.FindItemById(self.park_id).Check(True)
1391            self.menu1.FindItemById(self.scipy_id).Check(False)
1392        else:
1393            self.menu1.FindItemById(self.park_id).Check(False)
1394            self.menu1.FindItemById(self.scipy_id).Check(True)
1395        ## post a message to status bar
1396        msg = "Engine set to: %s" % self._fit_engine
1397        wx.PostEvent(self.parent, 
1398                     StatusEvent(status=msg))
1399        ## send the current engine type to fitpanel
1400        self.fit_panel._on_engine_change(name=self._fit_engine)
1401
1402       
1403    def _on_model_panel(self, evt):
1404        """
1405        react to model selection on any combo box or model menu.plot the model 
1406       
1407        :param evt: wx.combobox event
1408       
1409        """
1410        model = evt.model
1411        uid = evt.uid
1412        qmin = evt.qmin
1413        qmax = evt.qmax
1414        smearer = evt.smearer
1415        caption = evt.caption
1416        enable_smearer = evt.enable_smearer
1417        if model == None:
1418            return
1419        if uid not in self.page_finder.keys():
1420            return
1421        # save the name containing the data name with the appropriate model
1422        self.page_finder[uid].set_model(model)
1423        self.page_finder[uid].enable_smearing(enable_smearer)
1424        self.page_finder[uid].set_range(qmin=qmin, qmax=qmax)
1425        self.page_finder[uid].set_fit_tab_caption(caption=caption)
1426        if self.sim_page is not None and not self.batch_on:
1427            self.sim_page.draw_page()
1428       
1429    def _update1D(self, x, output):
1430        """
1431        Update the output of plotting model 1D
1432        """
1433        msg = "Plot updating ... "
1434        wx.PostEvent(self.parent, StatusEvent(status=msg,type="update"))
1435       
1436    def _complete1D(self, x, y, page_id, elapsed, index, model,
1437                    weight=None, fid=None,
1438                    toggle_mode_on=False, state=None, 
1439                    data=None, update_chisqr=True):
1440        """
1441        Complete plotting 1D data
1442        """ 
1443        try:
1444            new_plot = Data1D(x=x, y=y)
1445            new_plot.is_data = False
1446            new_plot.dy = numpy.zeros(len(y))
1447            new_plot.symbol = GUIFRAME_ID.CURVE_SYMBOL_NUM
1448            _yaxis, _yunit = data.get_yaxis() 
1449            _xaxis, _xunit = data.get_xaxis() 
1450            new_plot.title = data.name
1451
1452            new_plot.group_id = self.page_finder[page_id].get_graph_id()
1453            if new_plot.group_id == None:
1454                new_plot.group_id = data.group_id
1455            new_plot.id =  str(page_id) + "model"
1456            #if new_plot.id in self.color_dict:
1457            #    new_plot.custom_color = self.color_dict[new_plot.id]
1458            #find if this theory was already plotted and replace that plot given
1459            #the same id
1460            theory_data = self.page_finder[page_id].get_theory_data(fid=data.id)
1461            new_plot.name = model.name + " ["+ str(model.__class__.__name__)+"]"
1462            new_plot.xaxis(_xaxis, _xunit)
1463            new_plot.yaxis(_yaxis, _yunit)
1464            self.page_finder[page_id].set_theory_data(data=new_plot, 
1465                                                      fid=data.id)
1466            self.parent.update_theory(data_id=data.id, theory=new_plot,
1467                                       state=state)   
1468            current_pg = self.fit_panel.get_page_by_id(page_id)
1469            title = new_plot.title
1470            batch_on = self.fit_panel.get_page_by_id(page_id).batch_on
1471            if not batch_on:
1472                wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot,
1473                                            title=str(title)))
1474            caption = current_pg.window_caption
1475            self.page_finder[page_id].set_fit_tab_caption(caption=caption)
1476            try:
1477                # replace model cal to fit calculation if possible
1478                new_plot.y = self.page_finder[page_id].get_result(fid=data.id).theory
1479            except:
1480                pass
1481
1482            self.page_finder[page_id].set_theory_data(data=new_plot, 
1483                                                      fid=data.id)
1484            if toggle_mode_on:
1485                wx.PostEvent(self.parent, 
1486                             NewPlotEvent(group_id=str(page_id) + " Model2D",
1487                                               action="Hide"))
1488            else:
1489                if update_chisqr:
1490                    wx.PostEvent(current_pg,
1491                                 Chi2UpdateEvent(output=self._cal_chisqr(
1492                                                                data=data,
1493                                                                fid=fid,
1494                                                                weight=weight,
1495                                                            page_id=page_id,
1496                                                            index=index)))
1497                else:
1498                    self._plot_residuals(page_id=page_id, data=data, fid=fid,
1499                                          index=index, weight=weight)
1500
1501            msg = "Computation  completed!"
1502            wx.PostEvent( self.parent, StatusEvent(status=msg, type="stop" ))
1503        except:
1504            raise
1505            #msg = " Error occurred when drawing %s Model 1D: " % new_plot.name
1506            #msg += " %s"  % sys.exc_value
1507            #wx.PostEvent(self.parent, StatusEvent(status=msg, type="stop"))
1508   
1509    def _update2D(self, output,time=None):
1510        """
1511        Update the output of plotting model
1512        """
1513        wx.PostEvent(self.parent, StatusEvent(status="Plot \
1514        #updating ... ", type="update"))
1515        #self.ready_fit()
1516 
1517    def _complete2D(self, image, data, model, page_id,  elapsed, index, qmin,
1518                qmax, fid=None, weight=None, toggle_mode_on=False, state=None, 
1519                     update_chisqr=True):
1520        """
1521        Complete get the result of modelthread and create model 2D
1522        that can be plot.
1523        """
1524        new_plot= Data2D(image=image, err_image=data.err_data)
1525        new_plot.name = model.name
1526        new_plot.title = "Analytical model 2D "
1527        new_plot.id = str(page_id) + "model"
1528        new_plot.group_id = str(page_id) + " Model2D"
1529        new_plot.detector = data.detector
1530        new_plot.source = data.source
1531        new_plot.is_data = False 
1532        new_plot.qx_data = data.qx_data
1533        new_plot.qy_data = data.qy_data
1534        new_plot.q_data = data.q_data
1535        new_plot.mask = data.mask
1536        ## plot boundaries
1537        new_plot.ymin = data.ymin
1538        new_plot.ymax = data.ymax
1539        new_plot.xmin = data.xmin
1540        new_plot.xmax = data.xmax
1541        title = data.title
1542        if len(title) > 1:
1543            new_plot.title = "Model2D for " + data.name
1544        new_plot.is_data = False
1545        new_plot.name = model.name + " [" + \
1546                                    str(model.__class__.__name__) + "-2D]"
1547        theory_data = deepcopy(new_plot)
1548        theory_data.name = "Unknown"
1549       
1550        self.page_finder[page_id].set_theory_data(data=theory_data, fid=data.id)
1551        self.parent.update_theory(data_id=data.id, 
1552                                       theory=new_plot,
1553                                       state=state) 
1554        current_pg = self.fit_panel.get_page_by_id(page_id)
1555        title = new_plot.title
1556        wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot,
1557                                               title=title))
1558        try:
1559            # replace model cal to fit calculation if possible
1560            new_plot.data = self.page_finder[page_id].get_result(fid=data.id).theory
1561        except:
1562            pass
1563
1564        self.page_finder[page_id].set_theory_data(data=new_plot, fid=data.id)
1565        if toggle_mode_on:
1566            wx.PostEvent(self.parent, 
1567                             NewPlotEvent(group_id=str(page_id) + " Model1D",
1568                                               action="Hide"))
1569        else:
1570            # Chisqr in fitpage
1571            if update_chisqr:
1572                wx.PostEvent(current_pg,
1573                             Chi2UpdateEvent(output=self._cal_chisqr(data=data,
1574                                                                    weight=weight,
1575                                                                    fid=fid,
1576                                                         page_id=page_id,
1577                                                         index=index)))
1578            else:
1579                self._plot_residuals(page_id=page_id, data=data, fid=fid,
1580                                      index=index, weight=weight)
1581        msg = "Computation  completed!"
1582        wx.PostEvent(self.parent, StatusEvent(status=msg, type="stop"))
1583   
1584    def _draw_model2D(self, model, page_id, qmin,
1585                      qmax,
1586                      data=None, smearer=None,
1587                      description=None, enable2D=False,
1588                      state=None,
1589                      fid=None,
1590                      weight=None,
1591                      toggle_mode_on=False,
1592                       update_chisqr=True):
1593        """
1594        draw model in 2D
1595       
1596        :param model: instance of the model to draw
1597        :param description: the description of the model
1598        :param enable2D: when True allows to draw model 2D
1599        :param qmin: the minimum value to  draw model 2D
1600        :param qmax: the maximum value to draw model 2D
1601        :param qstep: the number of division of Qx and Qy of the model to draw
1602           
1603        """
1604        if not enable2D:
1605            return None
1606        try:
1607            from model_thread import Calc2D
1608            ## If a thread is already started, stop it
1609            if (self.calc_2D is not None) and self.calc_2D.isrunning():
1610                self.calc_2D.stop()
1611            self.calc_2D = Calc2D(model=model, 
1612                                    data=data,
1613                                    page_id=page_id,
1614                                    smearer=smearer,
1615                                    qmin=qmin,
1616                                    qmax=qmax,
1617                                    weight=weight, 
1618                                    fid=fid,
1619                                    toggle_mode_on=toggle_mode_on,
1620                                    state=state,
1621                                    completefn=self._complete2D,
1622                                    update_chisqr=update_chisqr)
1623            self.calc_2D.queue()
1624
1625        except:
1626            raise
1627            #msg = " Error occurred when drawing %s Model 2D: " % model.name
1628            #msg += " %s" % sys.exc_value
1629            #wx.PostEvent(self.parent, StatusEvent(status=msg))
1630
1631    def _draw_model1D(self, model, page_id, data, 
1632                      qmin, qmax, smearer=None,
1633                state=None,
1634                weight=None,
1635                fid=None, 
1636                toggle_mode_on=False, update_chisqr=True, 
1637                enable1D=True):
1638        """
1639        Draw model 1D from loaded data1D
1640       
1641        :param data: loaded data
1642        :param model: the model to plot
1643       
1644        """
1645        if not enable1D:
1646            return 
1647        try:
1648            from model_thread import Calc1D
1649            ## If a thread is already started, stop it
1650            if (self.calc_1D is not None) and self.calc_1D.isrunning():
1651                self.calc_1D.stop()
1652            self.calc_1D = Calc1D(data=data,
1653                                  model=model,
1654                                  page_id=page_id, 
1655                                  qmin=qmin,
1656                                  qmax=qmax,
1657                                  smearer=smearer,
1658                                  state=state,
1659                                  weight=weight,
1660                                  fid=fid,
1661                                  toggle_mode_on=toggle_mode_on,
1662                                  completefn=self._complete1D,
1663                                  #updatefn = self._update1D,
1664                                  update_chisqr=update_chisqr)
1665            self.calc_1D.queue()
1666        except:
1667            msg = " Error occurred when drawing %s Model 1D: " % model.name
1668            msg += " %s" % sys.exc_value
1669            wx.PostEvent(self.parent, StatusEvent(status=msg))
1670   
1671 
1672   
1673    def _cal_chisqr(self, page_id, data, weight, fid=None, index=None): 
1674        """
1675        Get handy Chisqr using the output from draw1D and 2D,
1676        instead of calling expansive CalcChisqr in guithread
1677        """
1678        data_copy = deepcopy(data) 
1679        # default chisqr
1680        chisqr = None
1681        #to compute chisq make sure data has valid data
1682        # return None if data == None
1683        if not check_data_validity(data_copy) or data_copy == None:
1684            return chisqr
1685
1686        # Get data: data I, theory I, and data dI in order
1687        if data_copy.__class__.__name__ == "Data2D":
1688            if index == None: 
1689                index = numpy.ones(len(data_copy.data),ntype=bool)
1690            if weight != None:
1691                data_copy.err_data = weight
1692            # get rid of zero error points
1693            index = index & (data_copy.err_data != 0) 
1694            index = index & (numpy.isfinite(data_copy.data)) 
1695            fn = data_copy.data[index] 
1696            theory_data = self.page_finder[page_id].get_theory_data(fid=data_copy.id)
1697            if theory_data== None:
1698                return chisqr
1699            gn = theory_data.data[index]
1700            en = data_copy.err_data[index]
1701        else:
1702            # 1 d theory from model_thread is only in the range of index
1703            if index == None: 
1704                index = numpy.ones(len(data_copy.y), ntype=bool)
1705            if weight != None:
1706                data_copy.dy = weight
1707            if data_copy.dy == None or data_copy.dy == []:
1708                dy = numpy.ones(len(data_copy.y))
1709            else:
1710                ## Set consitently w/AbstractFitengine:
1711                # But this should be corrected later.
1712                dy = deepcopy(data_copy.dy)
1713                dy[dy==0] = 1
1714            fn = data_copy.y[index] 
1715           
1716            theory_data = self.page_finder[page_id].get_theory_data(fid=data_copy.id)
1717            if theory_data== None:
1718                return chisqr
1719            gn = theory_data.y
1720            en = dy[index]
1721           
1722        # residual
1723        res = (fn - gn) / en
1724        residuals = res[numpy.isfinite(res)]
1725        # get chisqr only w/finite
1726        chisqr = numpy.average(residuals * residuals)
1727       
1728        self._plot_residuals(page_id=page_id, data=data_copy, 
1729                             fid=fid,
1730                             weight=weight, index=index)
1731       
1732        return chisqr
1733   
1734    def _plot_residuals(self, page_id, weight, fid=None,
1735                        data=None, index=None): 
1736        """
1737        Plot the residuals
1738       
1739        :param data: data
1740        :param index: index array (bool)
1741        : Note: this is different from the residuals in cal_chisqr()
1742        """
1743        data_copy = deepcopy(data)
1744        # Get data: data I, theory I, and data dI in order
1745        if data_copy.__class__.__name__ == "Data2D":
1746            # build residuals
1747            residuals = Data2D()
1748            #residuals.copy_from_datainfo(data)
1749            # Not for trunk the line below, instead use the line above
1750            data_copy.clone_without_data(len(data_copy.data), residuals)
1751            residuals.data = None
1752            fn = data_copy.data#[index]
1753            theory_data = self.page_finder[page_id].get_theory_data(fid=data_copy.id)
1754            gn = theory_data.data#[index]
1755            if weight == None:
1756                en = data_copy.err_data
1757            else:
1758                en = weight
1759            residuals.data = (fn - gn) / en
1760            residuals.qx_data = data_copy.qx_data#[index]
1761            residuals.qy_data = data_copy.qy_data #[index]
1762            residuals.q_data = data_copy.q_data#[index]
1763            residuals.err_data = numpy.ones(len(residuals.data))#[index]
1764            residuals.xmin = min(residuals.qx_data)
1765            residuals.xmax = max(residuals.qx_data)
1766            residuals.ymin = min(residuals.qy_data)
1767            residuals.ymax = max(residuals.qy_data)
1768            residuals.q_data = data_copy.q_data#[index]
1769            residuals.mask = data_copy.mask
1770            residuals.scale = 'linear'
1771            # check the lengths
1772            if len(residuals.data) != len(residuals.q_data):
1773                return
1774        else:
1775            # 1 d theory from model_thread is only in the range of index
1776            if data_copy.dy == None or data_copy.dy == []:
1777                dy = numpy.ones(len(data_copy.y))
1778            else:
1779                if weight == None:
1780                    dy = numpy.ones(len(data_copy.y))
1781                ## Set consitently w/AbstractFitengine:
1782                ## But this should be corrected later.
1783                else:
1784                    dy = weight#deepcopy(data_copy.dy)
1785                dy[dy==0] = 1 
1786            fn = data_copy.y[index] 
1787            theory_data = self.page_finder[page_id].get_theory_data(fid=data_copy.id)
1788            gn = theory_data.y
1789            en = dy[index]
1790            # build residuals
1791            residuals = Data1D()
1792            residuals.y = (fn - gn) / en
1793            residuals.x = data_copy.x[index]
1794            residuals.dy = numpy.ones(len(residuals.y))
1795            residuals.dx = None
1796            residuals.dxl = None
1797            residuals.dxw = None
1798            residuals.ytransform = 'y'
1799            # For latter scale changes
1800            residuals.xaxis('\\rm{Q} ', 'A^{-1}')
1801            residuals.yaxis('\\rm{Residuals} ', 'normalized')
1802        new_plot = residuals
1803        new_plot.name = "Residuals for " + str(data.name)
1804        ## allow to highlight data when plotted
1805        new_plot.interactive = True
1806        ## when 2 data have the same id override the 1 st plotted
1807        new_plot.id = "res" + str(data_copy.id)#name + " residuals"
1808        ##group_id specify on which panel to plot this data
1809        group_id = self.page_finder[page_id].get_graph_id()
1810        if group_id == None:
1811            group_id = data.group_id
1812        new_plot.group_id ="res" + str(group_id)
1813        #new_plot.is_data = True
1814        ##post data to plot
1815        title = new_plot.name
1816        self.page_finder[page_id].set_residuals(residuals=new_plot, fid=data.id)
1817        batch_on = self.fit_panel.get_page_by_id(page_id).batch_on
1818        if not batch_on:
1819            wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot, title=title))
1820            return
1821        #reset weight 
1822        #self.weight = None
1823        # Need all residuals before plotting
1824        # Should be refactored
1825        n = len(self.page_finder[page_id].keys())
1826        m = self.page_finder[page_id].nbr_residuals_computed
1827        flag = False
1828        batch_inputs, batch_outputs = self.page_finder[page_id].get_batch_result()
1829         
1830        if self.page_finder[page_id].nbr_residuals_computed == -1:
1831            flag = False
1832        else:
1833            if m == n -1:
1834                flag = True
1835            else:
1836                flag = False
1837            self.page_finder[page_id].nbr_residuals_computed += 1
1838           
1839            self.on_set_batch_result(page_id=page_id,
1840                                 fid=fid,
1841                              batch_outputs=batch_outputs, 
1842                                batch_inputs=batch_inputs) 
1843        # plot data
1844        event = BatchDrawEvent(page_id=page_id , 
1845                           batch_on=batch_on,
1846                           fid=fid,
1847                           batch_outputs=batch_outputs,
1848                           batch_inputs=batch_inputs,
1849                           is_displayed=flag)
1850        wx.PostEvent(self.parent, event)
1851       
1852
1853       
1854    def on_display_grid(self, event):
1855        """
1856        deplay batch result
1857        """
1858        fid = event.fid
1859        page_id = event.page_id
1860        batch_on = event.batch_on
1861        flag = event.is_displayed
1862        batch_outputs = event.batch_outputs
1863        batch_inputs = event.batch_inputs
1864        if flag and batch_on and fid is not None:
1865            self.parent.on_set_batch_result(data_outputs=batch_outputs, 
1866                                            data_inputs=batch_inputs,
1867                                            plugin_name=self.sub_menu)
1868            self.page_finder[page_id].nbr_residuals_computed = -1
1869       
1870#def profile(fn, *args, **kw):
1871#    import cProfile, pstats, os
1872#    global call_result
1873#    def call():
1874#        global call_result
1875#        call_result = fn(*args, **kw)
1876#    cProfile.runctx('call()', dict(call=call), {}, 'profile.out')
1877#    stats = pstats.Stats('profile.out')
1878#    #stats.sort_stats('time')
1879#    stats.sort_stats('calls')
1880#    stats.print_stats()
1881#    os.unlink('profile.out')
1882#    return call_result
1883if __name__ == "__main__":
1884    i = Plugin()
1885   
1886   
1887   
1888   
Note: See TracBrowser for help on using the repository browser.