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

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

happen residuals to data_explorer

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