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

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

added chain Fit option for batch

  • Property mode set to 100644
File size: 77.6 KB
Line 
1
2
3################################################################################
4#This software was developed by the University of Tennessee as part of the
5#Distributed Data Analysis of Neutron Scattering Experiments (DANSE)
6#project funded by the US National Science Foundation.
7#
8#See the license text in license.txt
9#
10#copyright 2009, University of Tennessee
11################################################################################
12
13
14import re
15import sys
16import wx
17import logging
18import numpy
19import string
20import time
21from copy import deepcopy
22import models
23import fitpage
24
25
26from sans.dataloader.loader import Loader
27from sans.guiframe.dataFitting import Data2D
28from sans.guiframe.dataFitting import Data1D
29from sans.guiframe.dataFitting import check_data_validity
30from sans.guiframe.events import NewPlotEvent
31from sans.guiframe.events import StatusEvent 
32from sans.guiframe.events import EVT_SLICER_PANEL
33from sans.guiframe.events import EVT_SLICER_PARS_UPDATE
34from sans.guiframe.gui_style import GUIFRAME_ID
35from sans.guiframe.plugin_base import PluginBase
36from sans.guiframe.data_processor import BatchCell
37from sans.fit.Fitting import Fit
38from .console import ConsoleUpdate
39from .fitproblem import FitProblemDictionary
40from .fitpanel import FitPanel
41from .fit_thread import FitThread
42from .pagestate import Reader
43from .fitpage import Chi2UpdateEvent
44
45MAX_NBR_DATA = 4
46SANS_F_TOL = 5e-05
47
48(PageInfoEvent, EVT_PAGE_INFO)   = wx.lib.newevent.NewEvent()
49
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                        #Separate result in to data corresponding to each page
1114                        temp_pars = []
1115                        temp_res_param = []
1116                        # Park sorts the params by itself so that we must check
1117                        # param name and resort it back as it was. No effects on Scipy.
1118                        if res.parameters != None:
1119                            model = cpage.model
1120                            for fid in self.page_finder[pid]:
1121                                if fid != None:
1122                                    # Below works only for batch using one model
1123                                    model = self.page_finder[pid][fid].get_model()
1124                                    break
1125                            for p in res.parameters:
1126                                model_name, param_name = self.split_string(p.name) 
1127                                if model.name == model_name:
1128                                    p_name= model.name+"."+param_name
1129                                    if p.name == p_name:     
1130                                        temp_res_param.append(p)
1131                                        temp_pars.append(param_name)
1132                            res.parameters = temp_res_param
1133                            pars = temp_pars
1134                        cell = BatchCell()
1135                        cell.label = res.fitness
1136                        cell.value = res.fitness
1137                        batch_outputs["Chi2"].append(cell)
1138                        # add parameters to batch_results
1139                        param_list = model.getParamList()
1140                        for param in model.getDispParamList():
1141                            if not model.is_fittable(param) and \
1142                                param in param_list:
1143                                param_list.remove(param)
1144                        for param in param_list:
1145                            # save value of  fixed parameters
1146                            if param not in batch_outputs.keys():
1147                                batch_outputs[param] = []
1148                            if param not in pars:
1149                                batch_outputs[str(param)].append(model.getParam(param))
1150                        for index  in range(len(pars)):
1151                            #save only fitted values
1152                            batch_outputs[pars[index]].append(res.pvec[index])
1153                            item = res.stderr[index]
1154                            batch_inputs["error on %s" % pars[index]].append(item)
1155                            if pars[index] in model.getParamList():
1156                                model.setParam(pars[index], res.pvec[index])
1157                                   
1158                    self.page_finder[pid].set_batch_result(batch_inputs=batch_inputs,
1159                                                         batch_outputs=batch_outputs)   
1160                    cpage = self.fit_panel.get_page_by_id(pid)
1161                    cpage._on_fit_complete()
1162                    self.page_finder[pid][data.id].set_result(res)
1163                    fitproblem = self.page_finder[pid][data.id]
1164                    qmin, qmax = fitproblem.get_range()
1165                    flag = issubclass(data.__class__, Data2D)
1166                    correct_result = False
1167                    if not flag:
1168                        if len(res.theory) == len(res.index) and \
1169                            len(res.index) == len(data.y):
1170                            correct_result = True
1171                            self._complete1D(x=data.x, y=res.theory, page_id=pid, 
1172                                         elapsed=None, 
1173                                         index=res.index, model=model,
1174                                         weight=None, fid=data.id,
1175                                         toggle_mode_on=False, state=None, 
1176                                         data=data, update_chisqr=False, 
1177                                         source='fit')
1178                        else:
1179                            data_name = str(None)
1180                            if data is not None:
1181                                data_name = str(data.name)
1182                            model_name = str(None)
1183                            if model is not None:
1184                                model_name = str(model.name)
1185                            msg += "Data %s and Model %s did not fit.\n" % (data_name, 
1186                                                                            model_name)
1187                           
1188                    else:
1189                        if len(res.theory)== len(res.index) and \
1190                            len(res.index) == len(data.data):
1191                            correct_result = True
1192                            self._complete2D(image=res.theory, data=data,
1193                                          model=model,
1194                                          page_id=pid,  elapsed=None, 
1195                                          index=res.index, 
1196                                          qmin=qmin,
1197                                         qmax=qmax, fid=data.id, weight=None,
1198                                          toggle_mode_on=False, state=None, 
1199                                         update_chisqr=False, 
1200                                         source='fit')
1201                        else:
1202                            data_name = str(None)
1203                            if data is not None:
1204                                data_name = str(data.name)
1205                            model_name = str(None)
1206                            if model is not None:
1207                                model_name = str(model.name)
1208                            msg += "Data %s and Model %s did not fit.\n" % (data_name, 
1209                                                                            model_name)
1210                           
1211                    if correct_result : 
1212                        self.on_set_batch_result(page_id=pid, 
1213                                             fid=data.id, 
1214                                             batch_outputs=batch_outputs, 
1215                                             batch_inputs=batch_inputs)
1216         
1217        #print msg
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        theory_data.id = wx.NewId()
1245        theory_data.name = model.name + "[%s]" % str(model.__class__.__name__)
1246        cell.object = [data, theory_data]
1247        batch_outputs["Data"].append(cell)
1248        for key, value in data.meta_data.iteritems():
1249            if key not in batch_inputs.keys():
1250                batch_inputs[key] = []
1251            if key.lower().strip() != "loader":
1252                batch_inputs[key].append(value)
1253        param = "temperature"
1254        if hasattr(data.sample, param):
1255            if param not in  batch_inputs.keys():
1256                 batch_inputs[param] = []
1257            batch_inputs[param].append(data.sample.temperature)
1258        # associate residuals plot
1259        batch_outputs["Chi2"][index].object = [residuals]
1260       
1261   
1262    def _fit_completed(self, result, page_id, batch_outputs,
1263                             batch_inputs=None,
1264                              pars=None, 
1265                             elapsed=None):
1266        """
1267        Display result of the fit on related panel(s).
1268        :param result: list of object generated when fit ends
1269        :param pars: list of names of parameters fitted
1270        :param page_id: list of page ids which called fit function
1271        :param elapsed: time spent at the fitting level
1272        """
1273       # reset fit_engine if changed by simul_fit
1274        self._on_change_engine(self._gui_engine)
1275        result = result[0]
1276        self.fit_thread_list = {}
1277        if page_id is None:
1278            page_id = []
1279        ## fit more than 1 model at the same time
1280        self._mac_sleep(0.2) 
1281        try:
1282            index = 0
1283            for uid in page_id:
1284                res = result[index]
1285                if res.fitness is None or \
1286                    not numpy.isfinite(res.fitness) or \
1287                    numpy.any(res.pvec == None) or \
1288                    not numpy.all(numpy.isfinite(res.pvec)):
1289                    msg = "Fitting did not converge!!!"
1290                    wx.PostEvent(self.parent, 
1291                             StatusEvent(status=msg, 
1292                                         info="warning",
1293                                         type="stop"))
1294                    self._update_fit_button(page_id)
1295                else:
1296                    cpage = self.fit_panel.get_page_by_id(uid)
1297                    # Make sure we got all results
1298                    #(CallAfter is important to MAC)
1299                    wx.CallAfter(cpage.onsetValues, res.fitness, res.param_list, 
1300                             res.pvec, res.stderr)
1301                    index += 1
1302                    cpage._on_fit_complete()
1303                    if res.stderr == None:
1304                        msg = "Fit Abort: "
1305                    else:
1306                        msg = "Fitting: "
1307                    msg += "Completed!!!"
1308                    wx.PostEvent(self.parent, StatusEvent(status=msg))
1309        except ValueError:
1310                raise
1311                self._update_fit_button(page_id)
1312                msg = "Fitting did not converge!!!"
1313                wx.PostEvent(self.parent, StatusEvent(status=msg, info="error",
1314                                                      type="stop"))
1315        except:
1316            raise
1317            self._update_fit_button(page_id)
1318            msg = "Fit completed but Following"
1319            msg += " error occurred:%s" % sys.exc_value
1320            wx.PostEvent(self.parent, StatusEvent(status=msg, info="error",
1321                                                  type="stop"))
1322           
1323    def _update_fit_button(self, page_id):
1324        """
1325        Update Fit button when fit stopped
1326       
1327        : parameter page_id: fitpage where the button is
1328        """
1329        if page_id.__class__.__name__ != 'list':
1330            page_id = [page_id]
1331        for uid in page_id: 
1332            page = self.fit_panel.get_page_by_id(uid)
1333            page._on_fit_complete()
1334       
1335    def _on_show_panel(self, event):
1336        """
1337        """
1338        pass
1339   
1340    def on_reset_batch_flag(self, event):
1341        """
1342        Set batch_reset_flag
1343        """
1344        event.Skip()
1345        menu_item = self.menu1.FindItemById(self.id_reset_flag)
1346        flag = menu_item.IsChecked()
1347        if not flag:
1348            menu_item.Check(False)
1349            self.batch_reset_flag = True
1350        else:
1351            menu_item.Check(True)
1352            self.batch_reset_flag = False
1353       
1354        ## post a message to status bar
1355        msg = "Set Chain Fitting: %s" % str(not self.batch_reset_flag)
1356        wx.PostEvent(self.parent, 
1357                     StatusEvent(status=msg))
1358
1359    def _onset_engine_park(self,event):
1360        """
1361        set engine to park
1362        """
1363        self._on_change_engine('park')
1364       
1365    def _onset_engine_scipy(self,event):
1366        """
1367        set engine to scipy
1368        """
1369        self._on_change_engine('scipy')
1370       
1371    def _on_slicer_event(self, event):
1372        """
1373        Receive a panel as event and send it to guiframe
1374       
1375        :param event: event containing a panel
1376       
1377        """
1378        if event.panel is not None:
1379            new_panel = event.panel
1380            self.slicer_panels.append(event.panel)
1381            # Set group ID if available
1382            event_id = self.parent.popup_panel(new_panel)
1383            new_panel.uid = event_id
1384            self.mypanels.append(new_panel) 
1385       
1386    def _onclearslicer(self, event):
1387        """
1388        Clear the boxslicer when close the panel associate with this slicer
1389        """
1390        name =event.GetPane().caption
1391   
1392        for panel in self.slicer_panels:
1393            if panel.window_caption==name:
1394               
1395                for item in self.parent.panels:
1396                    if hasattr(self.parent.panels[item], "uid"):
1397                        if self.parent.panels[item].uid ==panel.base.uid:
1398                            self.parent.panels[item].onClearSlicer(event)
1399                            self.parent._mgr.Update()
1400                            break 
1401                break
1402   
1403    def _return_engine_type(self):
1404        """
1405        return the current type of engine
1406        """
1407        return self._fit_engine
1408     
1409     
1410    def _on_change_engine(self, engine='park'):
1411        """
1412        Allow to select the type of engine to perform fit
1413       
1414        :param engine: the key work of the engine
1415       
1416        """
1417        ## saving fit engine name
1418        self._fit_engine = engine
1419        ## change menu item state
1420        if engine == "park":
1421            self.menu1.FindItemById(self.park_id).Check(True)
1422            self.menu1.FindItemById(self.scipy_id).Check(False)
1423        else:
1424            self.menu1.FindItemById(self.park_id).Check(False)
1425            self.menu1.FindItemById(self.scipy_id).Check(True)
1426        ## post a message to status bar
1427        msg = "Engine set to: %s" % self._fit_engine
1428        wx.PostEvent(self.parent, 
1429                     StatusEvent(status=msg))
1430        ## send the current engine type to fitpanel
1431        self.fit_panel._on_engine_change(name=self._fit_engine)
1432
1433       
1434    def _on_model_panel(self, evt):
1435        """
1436        react to model selection on any combo box or model menu.plot the model 
1437       
1438        :param evt: wx.combobox event
1439       
1440        """
1441        model = evt.model
1442        uid = evt.uid
1443        qmin = evt.qmin
1444        qmax = evt.qmax
1445        smearer = evt.smearer
1446        caption = evt.caption
1447        enable_smearer = evt.enable_smearer
1448        if model == None:
1449            return
1450        if uid not in self.page_finder.keys():
1451            return
1452        # save the name containing the data name with the appropriate model
1453        self.page_finder[uid].set_model(model)
1454        self.page_finder[uid].enable_smearing(enable_smearer)
1455        self.page_finder[uid].set_range(qmin=qmin, qmax=qmax)
1456        self.page_finder[uid].set_fit_tab_caption(caption=caption)
1457        if self.sim_page is not None and not self.batch_on:
1458            self.sim_page.draw_page()
1459       
1460    def _update1D(self, x, output):
1461        """
1462        Update the output of plotting model 1D
1463        """
1464        msg = "Plot updating ... "
1465        wx.PostEvent(self.parent, StatusEvent(status=msg,type="update"))
1466       
1467    def _complete1D(self, x, y, page_id, elapsed, index, model,
1468                    weight=None, fid=None,
1469                    toggle_mode_on=False, state=None, 
1470                    data=None, update_chisqr=True, source='model'):
1471        """
1472        Complete plotting 1D data
1473        """ 
1474        try:
1475            new_plot = Data1D(x=x, y=y)
1476            new_plot.is_data = False
1477            new_plot.dy = numpy.zeros(len(y))
1478            new_plot.symbol = GUIFRAME_ID.CURVE_SYMBOL_NUM
1479            _yaxis, _yunit = data.get_yaxis() 
1480            _xaxis, _xunit = data.get_xaxis() 
1481            new_plot.title = data.name
1482
1483            new_plot.group_id = self.page_finder[page_id].get_graph_id()
1484            if new_plot.group_id == None:
1485                new_plot.group_id = data.group_id
1486            new_plot.id =  str(page_id) + "model"
1487            #if new_plot.id in self.color_dict:
1488            #    new_plot.custom_color = self.color_dict[new_plot.id]
1489            #find if this theory was already plotted and replace that plot given
1490            #the same id
1491            theory_data = self.page_finder[page_id].get_theory_data(fid=data.id)
1492            new_plot.name = model.name + " ["+ str(model.__class__.__name__)+"]"
1493            new_plot.xaxis(_xaxis, _xunit)
1494            new_plot.yaxis(_yaxis, _yunit)
1495            self.page_finder[page_id].set_theory_data(data=new_plot, 
1496                                                      fid=data.id)
1497            self.parent.update_theory(data_id=data.id, theory=new_plot,
1498                                       state=state)   
1499            current_pg = self.fit_panel.get_page_by_id(page_id)
1500            title = new_plot.title
1501            batch_on = self.fit_panel.get_page_by_id(page_id).batch_on
1502            if not batch_on:
1503                wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot,
1504                                            title=str(title)))
1505            else:
1506                top_data_id = self.fit_panel.get_page_by_id(page_id).data.id
1507                if data.id == top_data_id:
1508                    wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot,
1509                                            title=str(title)))   
1510            caption = current_pg.window_caption
1511            self.page_finder[page_id].set_fit_tab_caption(caption=caption)
1512           
1513            self.page_finder[page_id].set_theory_data(data=new_plot, 
1514                                                      fid=data.id)
1515            if toggle_mode_on:
1516                wx.PostEvent(self.parent, 
1517                             NewPlotEvent(group_id=str(page_id) + " Model2D",
1518                                               action="Hide"))
1519            else:
1520                if update_chisqr:
1521                    wx.PostEvent(current_pg,
1522                                 Chi2UpdateEvent(output=self._cal_chisqr(
1523                                                                data=data,
1524                                                                fid=fid,
1525                                                                weight=weight,
1526                                                            page_id=page_id,
1527                                                            index=index)))
1528                else:
1529                    self._plot_residuals(page_id=page_id, data=data, fid=fid,
1530                                          index=index, weight=weight)
1531
1532            msg = "Computation  completed!"
1533            wx.PostEvent( self.parent, StatusEvent(status=msg, type="stop" ))
1534        except:
1535            raise
1536            #msg = " Error occurred when drawing %s Model 1D: " % new_plot.name
1537            #msg += " %s"  % sys.exc_value
1538            #wx.PostEvent(self.parent, StatusEvent(status=msg, type="stop"))
1539   
1540    def _update2D(self, output,time=None):
1541        """
1542        Update the output of plotting model
1543        """
1544        wx.PostEvent(self.parent, StatusEvent(status="Plot \
1545        #updating ... ", type="update"))
1546        #self.ready_fit()
1547 
1548    def _complete2D(self, image, data, model, page_id,  elapsed, index, qmin,
1549                qmax, fid=None, weight=None, toggle_mode_on=False, state=None, 
1550                     update_chisqr=True, source='model'):
1551        """
1552        Complete get the result of modelthread and create model 2D
1553        that can be plot.
1554        """
1555        new_plot= Data2D(image=image, err_image=data.err_data)
1556        new_plot.name = model.name
1557        new_plot.title = "Analytical model 2D "
1558        new_plot.id = str(page_id) + "model"
1559        new_plot.group_id = str(page_id) + " Model2D"
1560        new_plot.detector = data.detector
1561        new_plot.source = data.source
1562        new_plot.is_data = False 
1563        new_plot.qx_data = data.qx_data
1564        new_plot.qy_data = data.qy_data
1565        new_plot.q_data = data.q_data
1566        new_plot.mask = data.mask
1567        ## plot boundaries
1568        new_plot.ymin = data.ymin
1569        new_plot.ymax = data.ymax
1570        new_plot.xmin = data.xmin
1571        new_plot.xmax = data.xmax
1572        title = data.title
1573        if len(title) > 1:
1574            new_plot.title = "Model2D for " + data.name
1575        new_plot.is_data = False
1576        new_plot.name = model.name + " [" + \
1577                                    str(model.__class__.__name__) + "-2D]"
1578        theory_data = deepcopy(new_plot)
1579        theory_data.name = "Unknown"
1580       
1581        self.page_finder[page_id].set_theory_data(data=theory_data, fid=data.id)
1582        self.parent.update_theory(data_id=data.id, 
1583                                       theory=new_plot,
1584                                       state=state) 
1585        current_pg = self.fit_panel.get_page_by_id(page_id)
1586        title = new_plot.title
1587        batch_on = self.fit_panel.get_page_by_id(page_id).batch_on
1588        if not source == 'fit':
1589            wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot,
1590                                               title=title))
1591        self.page_finder[page_id].set_theory_data(data=new_plot, fid=data.id)
1592        if toggle_mode_on:
1593            wx.PostEvent(self.parent, 
1594                             NewPlotEvent(group_id=str(page_id) + " Model1D",
1595                                               action="Hide"))
1596        else:
1597            # Chisqr in fitpage
1598            if update_chisqr:
1599                wx.PostEvent(current_pg,
1600                             Chi2UpdateEvent(output=self._cal_chisqr(data=data,
1601                                                                    weight=weight,
1602                                                                    fid=fid,
1603                                                         page_id=page_id,
1604                                                         index=index)))
1605            else:
1606                self._plot_residuals(page_id=page_id, data=data, fid=fid,
1607                                      index=index, weight=weight)
1608        msg = "Computation  completed!"
1609        wx.PostEvent(self.parent, StatusEvent(status=msg, type="stop"))
1610   
1611    def _draw_model2D(self, model, page_id, qmin,
1612                      qmax,
1613                      data=None, smearer=None,
1614                      description=None, enable2D=False,
1615                      state=None,
1616                      fid=None,
1617                      weight=None,
1618                      toggle_mode_on=False,
1619                       update_chisqr=True, source='model'):
1620        """
1621        draw model in 2D
1622       
1623        :param model: instance of the model to draw
1624        :param description: the description of the model
1625        :param enable2D: when True allows to draw model 2D
1626        :param qmin: the minimum value to  draw model 2D
1627        :param qmax: the maximum value to draw model 2D
1628        :param qstep: the number of division of Qx and Qy of the model to draw
1629           
1630        """
1631        if not enable2D:
1632            return None
1633        try:
1634            from model_thread import Calc2D
1635            ## If a thread is already started, stop it
1636            if (self.calc_2D is not None) and self.calc_2D.isrunning():
1637                self.calc_2D.stop()
1638            self.calc_2D = Calc2D(model=model, 
1639                                    data=data,
1640                                    page_id=page_id,
1641                                    smearer=smearer,
1642                                    qmin=qmin,
1643                                    qmax=qmax,
1644                                    weight=weight, 
1645                                    fid=fid,
1646                                    toggle_mode_on=toggle_mode_on,
1647                                    state=state,
1648                                    completefn=self._complete2D,
1649                                    update_chisqr=update_chisqr, source=source)
1650            self.calc_2D.queue()
1651
1652        except:
1653            raise
1654            #msg = " Error occurred when drawing %s Model 2D: " % model.name
1655            #msg += " %s" % sys.exc_value
1656            #wx.PostEvent(self.parent, StatusEvent(status=msg))
1657
1658    def _draw_model1D(self, model, page_id, data, 
1659                      qmin, qmax, smearer=None,
1660                state=None,
1661                weight=None,
1662                fid=None, 
1663                toggle_mode_on=False, update_chisqr=True, source='model',
1664                enable1D=True):
1665        """
1666        Draw model 1D from loaded data1D
1667       
1668        :param data: loaded data
1669        :param model: the model to plot
1670       
1671        """
1672        if not enable1D:
1673            return 
1674        try:
1675            from model_thread import Calc1D
1676            ## If a thread is already started, stop it
1677            if (self.calc_1D is not None) and self.calc_1D.isrunning():
1678                self.calc_1D.stop()
1679            self.calc_1D = Calc1D(data=data,
1680                                  model=model,
1681                                  page_id=page_id, 
1682                                  qmin=qmin,
1683                                  qmax=qmax,
1684                                  smearer=smearer,
1685                                  state=state,
1686                                  weight=weight,
1687                                  fid=fid,
1688                                  toggle_mode_on=toggle_mode_on,
1689                                  completefn=self._complete1D,
1690                                  #updatefn = self._update1D,
1691                                  update_chisqr=update_chisqr,
1692                                  source=source)
1693            self.calc_1D.queue()
1694        except:
1695            msg = " Error occurred when drawing %s Model 1D: " % model.name
1696            msg += " %s" % sys.exc_value
1697            wx.PostEvent(self.parent, StatusEvent(status=msg))
1698   
1699 
1700   
1701    def _cal_chisqr(self, page_id, data, weight, fid=None, index=None): 
1702        """
1703        Get handy Chisqr using the output from draw1D and 2D,
1704        instead of calling expansive CalcChisqr in guithread
1705        """
1706        data_copy = deepcopy(data) 
1707        # default chisqr
1708        chisqr = None
1709        #to compute chisq make sure data has valid data
1710        # return None if data == None
1711        if not check_data_validity(data_copy) or data_copy == None:
1712            return chisqr
1713
1714        # Get data: data I, theory I, and data dI in order
1715        if data_copy.__class__.__name__ == "Data2D":
1716            if index == None: 
1717                index = numpy.ones(len(data_copy.data),ntype=bool)
1718            if weight != None:
1719                data_copy.err_data = weight
1720            # get rid of zero error points
1721            index = index & (data_copy.err_data != 0) 
1722            index = index & (numpy.isfinite(data_copy.data)) 
1723            fn = data_copy.data[index] 
1724            theory_data = self.page_finder[page_id].get_theory_data(fid=data_copy.id)
1725            if theory_data== None:
1726                return chisqr
1727            gn = theory_data.data[index]
1728            en = data_copy.err_data[index]
1729        else:
1730            # 1 d theory from model_thread is only in the range of index
1731            if index == None: 
1732                index = numpy.ones(len(data_copy.y), ntype=bool)
1733            if weight != None:
1734                data_copy.dy = weight
1735            if data_copy.dy == None or data_copy.dy == []:
1736                dy = numpy.ones(len(data_copy.y))
1737            else:
1738                ## Set consitently w/AbstractFitengine:
1739                # But this should be corrected later.
1740                dy = deepcopy(data_copy.dy)
1741                dy[dy==0] = 1
1742            fn = data_copy.y[index] 
1743           
1744            theory_data = self.page_finder[page_id].get_theory_data(fid=data_copy.id)
1745            if theory_data== None:
1746                return chisqr
1747            gn = theory_data.y
1748            en = dy[index]
1749           
1750        # residual
1751        res = (fn - gn) / en
1752        residuals = res[numpy.isfinite(res)]
1753        # get chisqr only w/finite
1754        chisqr = numpy.average(residuals * residuals)
1755       
1756        self._plot_residuals(page_id=page_id, data=data_copy, 
1757                             fid=fid,
1758                             weight=weight, index=index)
1759       
1760        return chisqr
1761   
1762    def _plot_residuals(self, page_id, weight, fid=None,
1763                        data=None, index=None): 
1764        """
1765        Plot the residuals
1766       
1767        :param data: data
1768        :param index: index array (bool)
1769        : Note: this is different from the residuals in cal_chisqr()
1770        """
1771        data_copy = deepcopy(data)
1772        # Get data: data I, theory I, and data dI in order
1773        if data_copy.__class__.__name__ == "Data2D":
1774            # build residuals
1775            residuals = Data2D()
1776            #residuals.copy_from_datainfo(data)
1777            # Not for trunk the line below, instead use the line above
1778            data_copy.clone_without_data(len(data_copy.data), residuals)
1779            residuals.data = None
1780            fn = data_copy.data#[index]
1781            theory_data = self.page_finder[page_id].get_theory_data(fid=data_copy.id)
1782            gn = theory_data.data#[index]
1783            if weight == None:
1784                en = data_copy.err_data
1785            else:
1786                en = weight
1787            residuals.data = (fn - gn) / en
1788            residuals.qx_data = data_copy.qx_data#[index]
1789            residuals.qy_data = data_copy.qy_data #[index]
1790            residuals.q_data = data_copy.q_data#[index]
1791            residuals.err_data = numpy.ones(len(residuals.data))#[index]
1792            residuals.xmin = min(residuals.qx_data)
1793            residuals.xmax = max(residuals.qx_data)
1794            residuals.ymin = min(residuals.qy_data)
1795            residuals.ymax = max(residuals.qy_data)
1796            residuals.q_data = data_copy.q_data#[index]
1797            residuals.mask = data_copy.mask
1798            residuals.scale = 'linear'
1799            # check the lengths
1800            if len(residuals.data) != len(residuals.q_data):
1801                return
1802        else:
1803            # 1 d theory from model_thread is only in the range of index
1804            if data_copy.dy == None or data_copy.dy == []:
1805                dy = numpy.ones(len(data_copy.y))
1806            else:
1807                if weight == None:
1808                    dy = numpy.ones(len(data_copy.y))
1809                ## Set consitently w/AbstractFitengine:
1810                ## But this should be corrected later.
1811                else:
1812                    dy = weight#deepcopy(data_copy.dy)
1813                dy[dy==0] = 1 
1814            fn = data_copy.y[index] 
1815            theory_data = self.page_finder[page_id].get_theory_data(fid=data_copy.id)
1816            gn = theory_data.y
1817            en = dy[index]
1818            # build residuals
1819            residuals = Data1D()
1820            residuals.y = (fn - gn) / en
1821            residuals.x = data_copy.x[index]
1822            residuals.dy = numpy.ones(len(residuals.y))
1823            residuals.dx = None
1824            residuals.dxl = None
1825            residuals.dxw = None
1826            residuals.ytransform = 'y'
1827            # For latter scale changes
1828            residuals.xaxis('\\rm{Q} ', 'A^{-1}')
1829            residuals.yaxis('\\rm{Residuals} ', 'normalized')
1830        new_plot = residuals
1831        new_plot.name = "Residuals for " + str(data.name)
1832        ## allow to highlight data when plotted
1833        new_plot.interactive = True
1834        ## when 2 data have the same id override the 1 st plotted
1835        new_plot.id = "res" + str(data_copy.id)#name + " residuals"
1836        ##group_id specify on which panel to plot this data
1837        group_id = self.page_finder[page_id].get_graph_id()
1838        if group_id == None:
1839            group_id = data.group_id
1840        new_plot.group_id ="res" + str(group_id)
1841        #new_plot.is_data = True
1842        ##post data to plot
1843        title = new_plot.name
1844        self.page_finder[page_id].set_residuals(residuals=new_plot, fid=data.id)
1845        batch_on = self.fit_panel.get_page_by_id(page_id).batch_on
1846        if not batch_on:
1847            wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot, title=title))
1848     
1849       
1850#def profile(fn, *args, **kw):
1851#    import cProfile, pstats, os
1852#    global call_result
1853#    def call():
1854#        global call_result
1855#        call_result = fn(*args, **kw)
1856#    cProfile.runctx('call()', dict(call=call), {}, 'profile.out')
1857#    stats = pstats.Stats('profile.out')
1858#    #stats.sort_stats('time')
1859#    stats.sort_stats('calls')
1860#    stats.print_stats()
1861#    os.unlink('profile.out')
1862#    return call_result
1863if __name__ == "__main__":
1864    i = Plugin()
1865   
1866   
1867   
1868   
Note: See TracBrowser for help on using the repository browser.