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

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

fixed batchfit with park

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