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

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

make sure a new group id is created for 2d data on batch

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