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

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

minor bug fixed together

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