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

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

make sure the model for each fit problem is a a new copy of the actual model not a reference

  • Property mode set to 100644
File size: 69.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.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                               toggle_mode_on=toggle_mode_on,
666                               state=state,
667                               update_chisqr=update_chisqr)
668        else:     
669            ## draw model 2D with no initial data
670            self._draw_model2D(model=model,
671                                page_id=page_id,
672                                data=data,
673                                enable2D=enable2D,
674                                smearer=smearer,
675                                qmin=qmin,
676                                qmax=qmax,
677                                state=state,
678                                toggle_mode_on=toggle_mode_on,
679                                update_chisqr=update_chisqr)
680           
681    def onFit(self, uid):
682        """
683        Get series of data, model, associates parameters and range and send then
684        to  series of fit engines. Fit data and model, display result to
685        corresponding panels.
686        :param uid: id related to the panel currently calling this fit function.
687        """
688        ##  count the number of fitproblem schedule to fit
689        fitproblem_count = 0
690        for value in self.page_finder.values():
691            if value.get_scheduled() == 1:
692                fitproblem_count += 1
693        ## if simultaneous fit change automatically the engine to park
694        if fitproblem_count > 1:
695            self._on_change_engine(engine='park')
696        self.fitproblem_count = fitproblem_count 
697        if self._fit_engine == "park":
698            engineType = "Simultaneous Fit"
699        else:
700            engineType = "Single Fit"
701        fitter_list = []       
702        sim_fitter = None     
703        if self.sim_page is not None and self.sim_page.uid == uid:
704            #simulatanous fit only one engine need to be created   
705            sim_fitter = Fit(self._fit_engine) 
706            fitter_list.append(sim_fitter) 
707       
708        self.current_pg = None
709        list_page_id = []
710        fit_id = 0
711        batch_inputs = {}
712        batch_outputs = {}
713        for page_id, value in self.page_finder.iteritems():
714            # For simulfit (uid give with None), do for-loop
715            # if uid is specified (singlefit), do it only on the page.
716            if engineType == "Single Fit":
717                if page_id != uid:
718                    continue
719            try:
720                if value.get_scheduled() == 1:
721                    #Get list of parameters name to fit
722                    pars = []
723                    templist = []
724                    page = self.fit_panel.get_page_by_id(page_id)
725                    self.set_fit_weight(uid=page.uid, 
726                                     flag=page.get_weight_flag(),
727                                     is2d = page._is_2D())
728                    templist = page.get_param_list()
729                    for element in templist:
730                        name = str(element[1])
731                        pars.append(name)
732                    for fitproblem in  value.get_fit_problem():
733                        if sim_fitter is None:
734                            fitter = Fit(self._fit_engine) 
735                            self._fit_helper(fitproblem=fitproblem, 
736                                                pars=pars, 
737                                                fitter=fitter,
738                                              fit_id=fit_id, 
739                                              batch_inputs=batch_inputs,
740                                              batch_outputs=batch_outputs)
741                            fitter_list.append(fitter) 
742                        else:
743                            fitter = sim_fitter
744                            self._fit_helper(fitproblem=fitproblem, 
745                                                pars=pars, 
746                                                fitter=fitter,
747                                              fit_id=fit_id, 
748                                              batch_inputs=batch_inputs,
749                                              batch_outputs=batch_outputs)
750                        fit_id += 1
751                    list_page_id.append(page_id)
752                    current_page_id = page_id
753                    value.clear_model_param()
754            except:
755                raise
756                msg= "%s error: %s" % (engineType, sys.exc_value)
757                wx.PostEvent(self.parent, StatusEvent(status=msg, info="error",
758                                                      type="stop"))
759                return 
760        ## If a thread is already started, stop it
761        #if self.calc_fit!= None and self.calc_fit.isrunning():
762        #    self.calc_fit.stop()
763        msg = "Fitting is in progress..."
764        wx.PostEvent( self.parent, StatusEvent(status=msg, type="progress" ))
765       
766        #Handler used for park engine displayed message
767        handler = ConsoleUpdate(parent=self.parent,
768                                manager=self,
769                                improvement_delta=0.1)
770        self._mac_sleep(0.2)
771        ## perform single fit
772        if fitproblem_count == 1:
773            calc_fit = FitThread(handler = handler,
774                                    fn=fitter_list,
775                                    pars=pars,
776                                    batch_inputs=batch_inputs,
777                                    batch_outputs=batch_outputs,
778                                    page_id=list_page_id,
779                                    completefn=self._single_fit_completed,
780                                    ftol=self.ftol)
781        else:
782            current_page_id = self.sim_page.uid
783            ## Perform more than 1 fit at the time
784            calc_fit = FitThread(handler=handler,
785                                    fn=fitter_list,
786                                    batch_inputs=batch_inputs,
787                                    batch_outputs=batch_outputs,
788                                    page_id=list_page_id,
789                                    updatefn=handler.update_fit,
790                                    completefn=self._simul_fit_completed,
791                                    ftol=self.ftol)
792        self.fit_thread_list[current_page_id] = calc_fit
793        calc_fit.queue()
794        msg = "Fitting is in progress..."
795        wx.PostEvent( self.parent, StatusEvent(status=msg, type="progress" ))
796       
797        self.ready_fit(calc_fit=calc_fit)
798       
799    def ready_fit(self, calc_fit):
800        """
801        Ready for another fit
802        """
803        if self.fitproblem_count != None and self.fitproblem_count > 1:
804            calc_fit.ready(2.5)
805        else:
806            time.sleep(0.4)
807           
808    def remove_plot(self, uid, fid=None, theory=False):
809        """
810        remove model plot when a fit page is closed
811        :param uid: the id related to the fitpage to close
812        :param fid: the id of the fitproblem(data, model, range,etc)
813        """
814        if uid not in self.page_finder.keys():
815            return
816        fitproblemList = self.page_finder[uid].get_fit_problem(fid)
817        for fitproblem in fitproblemList:
818            data = fitproblem.get_fit_data()
819            model = fitproblem.get_model()
820            plot_id = None
821            if model is not None:
822                plot_id = data.id + name
823            if theory:
824                plot_id = data.id
825            group_id = data.group_id
826            wx.PostEvent(self.parent, NewPlotEvent(id=plot_id,
827                                                       group_id=group_id,
828                                                       action='remove'))
829           
830    def store_data(self, uid, data_list=None, caption=None):
831        """
832        Recieve a list of data and store them ans well as a caption of
833        the fit page where they come from.
834        :param uid: if related to a fit page
835        :param data_list: list of data to fit
836        :param caption: caption of the window related to these data
837        """
838        if data_list is None:
839            data_list = []
840       
841        self.page_finder[uid].set_fit_data(data=data_list)
842        if caption is not None:
843            self.page_finder[uid].set_fit_tab_caption(caption=caption)
844           
845    def on_add_new_page(self, event=None):
846        """
847        ask fit panel to create a new empty page
848        """
849        try:
850            page = self.fit_panel.add_empty_page()
851            page_caption = page.window_caption
852            # add data associated to the page created
853            if page != None: 
854                wx.PostEvent(self.parent, StatusEvent(status="Page Created",
855                                               info="info"))
856            else:
857                msg = "Page was already Created"
858                wx.PostEvent(self.parent, StatusEvent(status=msg,
859                                                       info="warning"))
860            self.set_top_panel()
861        except:
862            msg = "Creating Fit page: %s"%sys.exc_value
863            wx.PostEvent(self.parent, StatusEvent(status=msg, info="error"))
864       
865    def add_fit_page(self, data):
866        """
867        given a data, ask to the fitting panel to create a new fitting page,
868        get this page and store it into the page_finder of this plug-in
869        :param data: is a list of data
870        """
871        page = self.fit_panel.set_data(data)
872        # page could be None when loading state files
873        if page == None:
874            return page
875        page_caption = page.window_caption
876        #append Data1D to the panel containing its theory
877        #if theory already plotted
878        if page.uid in self.page_finder:
879            data = page.get_data()
880            theory_data = self.page_finder[page.uid].get_theory_data(data.id)
881            if issubclass(data.__class__, Data2D):
882                data.group_id = wx.NewId()
883                if theory_data is not None:
884                    group_id = str(page.uid) + " Model1D"
885                    wx.PostEvent(self.parent, 
886                             NewPlotEvent(group_id=group_id,
887                                               action="delete"))
888                    self.parent.update_data(prev_data=theory_data,
889                                             new_data=data)     
890            else:
891                if theory_data is not None:
892                    group_id = str(page.uid) + " Model2D"
893                    data.group_id = theory_data.group_id
894                    wx.PostEvent(self.parent, 
895                             NewPlotEvent(group_id=group_id,
896                                               action="delete"))
897                    self.parent.update_data(prev_data=theory_data,
898                                             new_data=data)   
899        self.store_data(uid=page.uid, data_list=page.get_data_list(), 
900                        caption=page.window_caption)
901        if self.sim_page is not None and not self.batch_on:
902            self.sim_page.draw_page()
903        return page
904           
905    def _onEVT_SLICER_PANEL(self, event):
906        """
907        receive and event telling to update a panel with a name starting with
908        event.panel_name. this method update slicer panel
909        for a given interactor.
910       
911        :param event: contains type of slicer , paramaters for updating
912            the panel and panel_name to find the slicer 's panel concerned.
913        """
914        for item in self.parent.panels:
915            name = event.panel_name
916            if self.parent.panels[item].window_caption.startswith(name):
917                self.parent.panels[item].set_slicer(event.type, event.params)
918               
919        self.parent._mgr.Update()
920   
921    def _closed_fitpage(self, event):   
922        """
923        request fitpanel to close a given page when its unique data is removed
924        from the plot. close fitpage only when the a loaded data is removed
925        """   
926        if event is None or event.data is None:
927            return
928        if hasattr(event.data,"is_data"):
929            if not event.data.is_data or \
930                event.data.__class__.__name__ == "Data1D":
931                self.fit_panel.close_page_with_data(event.data) 
932 
933    def _reset_schedule_problem(self, value=0, uid=None):
934        """
935        unschedule or schedule all fitproblem to be fit
936        """
937        # case that uid is not specified
938        if uid == None:
939            for page_id in self.page_finder.keys():
940                self.page_finder[page_id].schedule_tofit(value)
941        # when uid is given
942        else:
943            if uid in self.page_finder.keys():
944                self.page_finder[uid].schedule_tofit(value)
945               
946    def _fit_helper(self, fitproblem, pars, fitter, fit_id,
947                    batch_inputs, batch_outputs):
948        """
949        Create and set fit engine with series of data and model
950        :param pars: list of fittable parameters
951        :param fitter_list: list of fit engine
952        :param value:  structure storing data mapped to their model, range etc..
953        """
954        data = fitproblem.get_fit_data()
955        model = fitproblem.get_model()
956        smearer = fitproblem.get_smearer()
957        qmin, qmax = fitproblem.get_range()
958        #Extra list of parameters and their constraints
959        listOfConstraint = []
960        param = fitproblem.get_model_param()
961        if len(param) > 0:
962            for item in param:
963                ## check if constraint
964                if item[0] != None and item[1] != None:
965                    listOfConstraint.append((item[0],item[1]))
966        for param in model.getParamList():
967            if param not in pars:
968                if param not in batch_inputs.keys():
969                    batch_inputs[param] = []
970                if param not in model.non_fittable:
971                    batch_inputs[param].append(model.getParam(param))
972        new_model = deepcopy(model)
973        fitter.set_model(new_model, fit_id, pars, constraints=listOfConstraint)
974        fitter.set_data(data=data, id=fit_id, smearer=smearer, qmin=qmin, 
975                        qmax=qmax)
976        fitter.select_problem_for_fit(id=fit_id, value=1)
977        #fill batch result information
978        if "Data" not in batch_outputs.keys():
979            batch_outputs["Data"] = []
980        batch_outputs["Data"].append(data.name)
981        for key, value in data.meta_data.iteritems():
982            if key not in batch_inputs.keys():
983                batch_inputs[key] = []
984            batch_inputs[key].append(value)
985        param = "temperature"
986        if hasattr(data.sample, param):
987            if param not in  batch_inputs.keys():
988                 batch_inputs[param] = []
989            batch_inputs[param].append(data.sample.temperature)
990   
991    def _onSelect(self,event):
992        """
993        when Select data to fit a new page is created .Its reference is
994        added to self.page_finder
995        """
996        self.panel = event.GetEventObject()
997        Plugin.on_perspective(self, event=event)
998        self.select_data(self.panel)
999       
1000    def select_data(self, panel):
1001        """
1002        """
1003        self.panel = panel
1004        for plottable in self.panel.graph.plottables:
1005            if plottable.__class__.__name__ in ["Data1D", "Theory1D"]:
1006                data_id = self.panel.graph.selected_plottable
1007                if plottable == self.panel.plots[data_id]:
1008                    data = plottable
1009                    self.add_fit_page(data=[data])
1010                    return
1011            else:
1012                data = plottable
1013                self.add_fit_page(data=[data])
1014        self.set_top_panel()
1015           
1016    def update_fit(self, result=None, msg=""):
1017        """
1018        """
1019        print "update_fit result", result
1020       
1021    def _batch_single_fit_complete_helper(self, result, pars, page_id, 
1022                            batch_outputs, batch_inputs, elapsed=None):
1023        """
1024        Display fit result in batch
1025        :param result: list of objects received fromt fit engines
1026        :param pars: list of  fitted parameters names
1027        :param page_id: list of page ids which called fit function
1028        :param elapsed: time spent at the fitting level
1029        """
1030        self._update_fit_button(page_id)
1031        msg = "Single Fitting complete "
1032        wx.PostEvent(self.parent, StatusEvent(status=msg, info="info",
1033                                                      type="stop"))
1034        if batch_outputs is None:
1035            batch_outputs = {}
1036        if self.batch_on:
1037            # format batch_outputs
1038            batch_outputs["Chi2"] = []
1039            for index  in range(len(pars)):
1040                batch_outputs[pars[index]] = []
1041                batch_outputs["error on %s" % pars[index]] = []
1042            for res in result:
1043                if res is None:
1044                    null_value = numpy.nan
1045                    batch_outputs["Chi2"].append(null_value)
1046                    for index  in range(len(pars)):
1047                        batch_outputs[pars[index]].append(null_value)
1048                        item = null_value
1049                        batch_outputs["error on %s" % pars[index]].append(item)
1050                else:
1051                    batch_outputs["Chi2"].append(res.fitness)
1052                    for index  in range(len(pars)):
1053                        batch_outputs[pars[index]].append(res.pvec[index])
1054                        item = res.stderr[index]
1055                        batch_outputs["error on %s" % pars[index]].append(item)
1056            pid = page_id[0]
1057            self.page_finder[pid].set_result(result=batch_outputs)   
1058            self.parent.on_set_batch_result(data_outputs=batch_outputs, 
1059                                            data_inputs=batch_inputs,
1060                                            plugin_name=self.sub_menu)
1061            for uid in page_id:
1062                cpage = self.fit_panel.get_page_by_id(uid)
1063                cpage._on_fit_complete()
1064           
1065    def _single_fit_completed(self, result, pars, page_id, batch_outputs,
1066                          batch_inputs=None,  elapsed=None):
1067        """
1068         Display fit result on one page of the notebook.
1069        :param result: list of object generated when fit ends
1070        :param pars: list of names of parameters fitted
1071        :param page_id: list of page ids which called fit function
1072        :param elapsed: time spent at the fitting level
1073        """ 
1074        self._mac_sleep(0.2)
1075        uid = page_id[0]
1076        if uid in self.fit_thread_list.keys():
1077            del self.fit_thread_list[uid] 
1078         
1079        cpage = self.fit_panel.get_page_by_id(uid)
1080        if cpage.batch_on:
1081            wx.CallAfter(self._batch_single_fit_complete_helper,
1082                          result, pars, page_id, batch_outputs, 
1083                          batch_inputs, elapsed)
1084            return 
1085        else: 
1086            try:
1087                result = result[0]
1088                if result == None:
1089                    self._update_fit_button(page_id)
1090                    msg= "Single Fitting did not converge!!!"
1091                    wx.PostEvent(self.parent, 
1092                                 StatusEvent(status=msg, 
1093                                             info="warning",
1094                                             type="stop"))
1095                    return
1096                if not numpy.isfinite(result.fitness) or \
1097                        numpy.any(result.pvec == None) or \
1098                        not numpy.all(numpy.isfinite(result.pvec)):
1099                    msg = "Single Fitting did not converge!!!"
1100                    wx.PostEvent(self.parent, 
1101                                 StatusEvent(status=msg, 
1102                                             info="warning",
1103                                             type="stop"))
1104                    self._update_fit_button(page_id)
1105                    return
1106               
1107                for uid in page_id:
1108                    cpage = self.fit_panel.get_page_by_id(uid)
1109                    # Make sure we got all results
1110                    #(CallAfter is important to MAC)
1111                    wx.CallAfter(cpage.onsetValues, result.fitness, pars, 
1112                                 result.pvec, result.stderr)
1113                    cpage._on_fit_complete()
1114                if result.stderr == None:
1115                    msg = "Fit Abort: "
1116                else:
1117                    msg = "Fitting: "
1118                msg += "Completed!!!"
1119                wx.PostEvent(self.parent, StatusEvent(status=msg))
1120            except ValueError:
1121                self._update_fit_button(page_id)
1122                msg = "Single Fitting did not converge!!!"
1123                wx.PostEvent(self.parent, StatusEvent(status=msg, info="error",
1124                                                      type="stop"))
1125            except:
1126                self._update_fit_button(page_id)
1127                msg = "Single Fit completed but Following"
1128                msg += " error occurred:%s" % sys.exc_value
1129                wx.PostEvent(self.parent, StatusEvent(status=msg, info="error",
1130                                                      type="stop"))
1131                raise
1132               
1133    def _simul_fit_completed(self, result, page_id, batch_outputs,
1134                             batch_inputs=None,
1135                              pars=None, 
1136                             elapsed=None):
1137        """
1138        Display result of the fit on related panel(s).
1139        :param result: list of object generated when fit ends
1140        :param pars: list of names of parameters fitted
1141        :param page_id: list of page ids which called fit function
1142        :param elapsed: time spent at the fitting level
1143        """
1144        result = result[0]
1145        self.fit_thread_list = {}
1146        if page_id is None:
1147            page_id = []
1148        ## fit more than 1 model at the same time
1149        self._mac_sleep(0.2) 
1150        try:
1151            msg = "" 
1152            if result == None:
1153                self._update_fit_button(page_id)
1154                msg= "Complex Fitting did not converge!!!"
1155                wx.PostEvent(self.parent, StatusEvent(status=msg,
1156                                                      type="stop"))
1157                return
1158            if not numpy.isfinite(result.fitness) or \
1159                numpy.any(result.pvec == None) or not \
1160                numpy.all(numpy.isfinite(result.pvec)):
1161                self._update_fit_button(page_id)
1162                msg= "Simultaneous Fitting did not converge!!!"
1163                wx.PostEvent(self.parent, StatusEvent(status=msg,type="stop"))
1164                return
1165             
1166            for uid in page_id:   
1167                fpdict = self.page_finder[uid]
1168                for value in fpdict.itervalues():
1169                    model = value.get_model()
1170                    data =  value.get_fit_data()
1171                    small_param_name = []
1172                    small_out = []
1173                    small_cov = []
1174                    #Separate result in to data corresponding to each page
1175                    for p in result.parameters:
1176                        model_name, param_name = self.split_string(p.name) 
1177                        if model.name == model_name:
1178                            p_name= model.name+"."+param_name
1179                            if p.name == p_name:     
1180                                if p.value != None and numpy.isfinite(p.value):
1181                                    small_out.append(p.value)
1182                                    small_param_name.append(param_name)
1183                                    small_cov.append(p.stderr)
1184                # Display result on each page
1185                cpage = self.fit_panel.get_page_by_id(uid)
1186                wx.CallAfter(cpage.onsetValues, 
1187                                    result.fitness,
1188                                  small_param_name,
1189                                  small_out,small_cov)
1190                cpage._on_fit_complete()
1191                msg = "Fit completed!"
1192                wx.PostEvent(self.parent, StatusEvent(status=msg))
1193        except Exception:
1194            raise
1195            self._update_fit_button(page_id)
1196            msg = "Complex Fitting did not converge!!!"
1197            wx.PostEvent(self.parent, StatusEvent(status=msg, info="error",
1198                                                  type="stop"))
1199            return
1200        except:
1201            self._update_fit_button(page_id)
1202            msg = "Simultaneous Fit completed"
1203            msg += " but Following error occurred:%s" % sys.exc_value
1204            wx.PostEvent(self.parent, StatusEvent(status=msg, type="stop"))
1205   
1206    def _update_fit_button(self, page_id):
1207        """
1208        Update Fit button when fit stopped
1209       
1210        : parameter page_id: fitpage where the button is
1211        """
1212        if page_id.__class__.__name__ != 'list':
1213            page_id = [page_id]
1214        for uid in page_id: 
1215            page = self.fit_panel.get_page_by_id(uid)
1216            page._on_fit_complete()
1217       
1218    def _on_show_panel(self, event):
1219        """
1220        """
1221        pass
1222       
1223    def _onset_engine_park(self,event):
1224        """
1225        set engine to park
1226        """
1227        self._on_change_engine('park')
1228       
1229    def _onset_engine_scipy(self,event):
1230        """
1231        set engine to scipy
1232        """
1233        self._on_change_engine('scipy')
1234       
1235    def _on_slicer_event(self, event):
1236        """
1237        Receive a panel as event and send it to guiframe
1238       
1239        :param event: event containing a panel
1240       
1241        """
1242        if event.panel is not None:
1243            new_panel = event.panel
1244            self.slicer_panels.append(event.panel)
1245            # Set group ID if available
1246            event_id = self.parent.popup_panel(new_panel)
1247            new_panel.uid = event_id
1248            self.mypanels.append(new_panel) 
1249       
1250    def _onclearslicer(self, event):
1251        """
1252        Clear the boxslicer when close the panel associate with this slicer
1253        """
1254        name =event.GetPane().caption
1255   
1256        for panel in self.slicer_panels:
1257            if panel.window_caption==name:
1258               
1259                for item in self.parent.panels:
1260                    if hasattr(self.parent.panels[item], "uid"):
1261                        if self.parent.panels[item].uid ==panel.base.uid:
1262                            self.parent.panels[item].onClearSlicer(event)
1263                            self.parent._mgr.Update()
1264                            break 
1265                break
1266   
1267    def _return_engine_type(self):
1268        """
1269        return the current type of engine
1270        """
1271        return self._fit_engine
1272     
1273     
1274    def _on_change_engine(self, engine='park'):
1275        """
1276        Allow to select the type of engine to perform fit
1277       
1278        :param engine: the key work of the engine
1279       
1280        """
1281        ## saving fit engine name
1282        self._fit_engine = engine
1283        ## change menu item state
1284        if engine == "park":
1285            self.menu1.FindItemById(self.park_id).Check(True)
1286            self.menu1.FindItemById(self.scipy_id).Check(False)
1287        else:
1288            self.menu1.FindItemById(self.park_id).Check(False)
1289            self.menu1.FindItemById(self.scipy_id).Check(True)
1290        ## post a message to status bar
1291        msg = "Engine set to: %s" % self._fit_engine
1292        wx.PostEvent(self.parent, 
1293                     StatusEvent(status=msg))
1294        ## send the current engine type to fitpanel
1295        self.fit_panel._on_engine_change(name=self._fit_engine)
1296
1297       
1298    def _on_model_panel(self, evt):
1299        """
1300        react to model selection on any combo box or model menu.plot the model 
1301       
1302        :param evt: wx.combobox event
1303       
1304        """
1305        model = evt.model
1306        uid = evt.uid
1307        qmin = evt.qmin
1308        qmax = evt.qmax
1309        smearer = evt.smearer
1310        caption = evt.caption
1311        enable_smearer = evt.enable_smearer
1312        if model == None:
1313            return
1314        if uid not in self.page_finder.keys():
1315            return
1316        # save the name containing the data name with the appropriate model
1317        self.page_finder[uid].set_model(model)
1318        self.page_finder[uid].enable_smearing(enable_smearer)
1319        self.page_finder[uid].set_range(qmin=qmin, qmax=qmax)
1320        self.page_finder[uid].set_fit_tab_caption(caption=caption)
1321        if self.sim_page is not None and not self.batch_on:
1322            self.sim_page.draw_page()
1323       
1324    def _update1D(self, x, output):
1325        """
1326        Update the output of plotting model 1D
1327        """
1328        msg = "Plot updating ... "
1329        wx.PostEvent(self.parent, StatusEvent(status=msg,type="update"))
1330       
1331    def _complete1D(self, x, y, page_id, elapsed, index, model,
1332                    toggle_mode_on=False, state=None, 
1333                    data=None, update_chisqr=True):
1334        """
1335        Complete plotting 1D data
1336        """ 
1337        try:
1338            new_plot = Data1D(x=x, y=y)
1339            new_plot.is_data = False
1340            new_plot.dy = numpy.zeros(len(y))
1341            new_plot.symbol = GUIFRAME_ID.CURVE_SYMBOL_NUM
1342            _yaxis, _yunit = data.get_yaxis() 
1343            _xaxis, _xunit = data.get_xaxis() 
1344            new_plot.title = data.name
1345
1346            new_plot.group_id = self.page_finder[page_id].get_graph_id()
1347            if new_plot.group_id == None:
1348                new_plot.group_id = data.group_id
1349            new_plot.id =  str(page_id) + "model"
1350            #if new_plot.id in self.color_dict:
1351            #    new_plot.custom_color = self.color_dict[new_plot.id]
1352            #find if this theory was already plotted and replace that plot given
1353            #the same id
1354            theory_data = self.page_finder[page_id].get_theory_data(fid=data.id)
1355            new_plot.name = model.name + " ["+ str(model.__class__.__name__)+"]"
1356            new_plot.xaxis(_xaxis, _xunit)
1357            new_plot.yaxis(_yaxis, _yunit)
1358            self.page_finder[page_id].set_theory_data(data=new_plot, 
1359                                                      fid=data.id)
1360            self.parent.update_theory(data_id=data.id, theory=new_plot,
1361                                       state=state)   
1362            current_pg = self.fit_panel.get_page_by_id(page_id)
1363            title = new_plot.title
1364            wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot,
1365                                            title=str(title)))
1366            caption = current_pg.window_caption
1367            self.page_finder[page_id].set_fit_tab_caption(caption=caption)
1368            self.page_finder[page_id].set_theory_data(data=new_plot, 
1369                                                      fid=data.id)
1370            if toggle_mode_on:
1371                wx.PostEvent(self.parent, 
1372                             NewPlotEvent(group_id=str(page_id) + " Model2D",
1373                                               action="Hide"))
1374            else:
1375                if update_chisqr:
1376                    wx.PostEvent(current_pg,
1377                                 Chi2UpdateEvent(output=self._cal_chisqr(
1378                                                                data=data,
1379                                                            page_id=page_id,
1380                                                            index=index)))
1381                else:
1382                    self._plot_residuals(page_id, data, index)
1383
1384            msg = "Computation  completed!"
1385            wx.PostEvent( self.parent, StatusEvent(status=msg, type="stop" ))
1386        except:
1387            raise
1388            #msg = " Error occurred when drawing %s Model 1D: " % new_plot.name
1389            #msg += " %s"  % sys.exc_value
1390            #wx.PostEvent(self.parent, StatusEvent(status=msg, type="stop"))
1391   
1392    def _update2D(self, output,time=None):
1393        """
1394        Update the output of plotting model
1395        """
1396        wx.PostEvent(self.parent, StatusEvent(status="Plot \
1397        #updating ... ", type="update"))
1398        #self.ready_fit()
1399 
1400    def _complete2D(self, image, data, model, page_id,  elapsed, index, qmin,
1401                     qmax, toggle_mode_on=False, state=None, 
1402                     update_chisqr=True):
1403        """
1404        Complete get the result of modelthread and create model 2D
1405        that can be plot.
1406        """
1407        new_plot= Data2D(image=image, err_image=data.err_data)
1408        new_plot.name = model.name
1409        new_plot.title = "Analytical model 2D "
1410        new_plot.id = str(page_id) + "model"
1411        new_plot.group_id = str(page_id) + " Model2D"
1412        new_plot.detector = data.detector
1413        new_plot.source = data.source
1414        new_plot.is_data = False 
1415        new_plot.qx_data = data.qx_data
1416        new_plot.qy_data = data.qy_data
1417        new_plot.q_data = data.q_data
1418        new_plot.mask = data.mask
1419        ## plot boundaries
1420        new_plot.ymin = data.ymin
1421        new_plot.ymax = data.ymax
1422        new_plot.xmin = data.xmin
1423        new_plot.xmax = data.xmax
1424        title = data.title
1425        if len(title) > 1:
1426            new_plot.title = "Model2D for " + data.name
1427        new_plot.is_data = False
1428        new_plot.name = model.name + " [" + \
1429                                    str(model.__class__.__name__) + "-2D]"
1430        theory_data = deepcopy(new_plot)
1431        theory_data.name = "Unknown"
1432       
1433        self.page_finder[page_id].set_theory_data(data=theory_data, fid=data.id)
1434        self.parent.update_theory(data_id=data.id, 
1435                                       theory=new_plot,
1436                                       state=state) 
1437        current_pg = self.fit_panel.get_page_by_id(page_id)
1438        title = new_plot.title
1439        wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot,
1440                                               title=title))
1441        self.page_finder[page_id].set_theory_data(data=new_plot, fid=data.id)
1442        if toggle_mode_on:
1443            wx.PostEvent(self.parent, 
1444                             NewPlotEvent(group_id=str(page_id) + " Model1D",
1445                                               action="Hide"))
1446        else:
1447            # Chisqr in fitpage
1448            if update_chisqr:
1449                wx.PostEvent(current_pg,
1450                             Chi2UpdateEvent(output=self._cal_chisqr(data=data,
1451                                                         page_id=page_id,
1452                                                         index=index)))
1453            else:
1454                self._plot_residuals(page_id, data, index)
1455        msg = "Computation  completed!"
1456        wx.PostEvent(self.parent, StatusEvent(status=msg, type="stop"))
1457   
1458    def _draw_model2D(self, model, page_id, qmin,
1459                      qmax,
1460                      data=None, smearer=None,
1461                      description=None, enable2D=False,
1462                      state=None,
1463                      toggle_mode_on=False,
1464                       update_chisqr=True):
1465        """
1466        draw model in 2D
1467       
1468        :param model: instance of the model to draw
1469        :param description: the description of the model
1470        :param enable2D: when True allows to draw model 2D
1471        :param qmin: the minimum value to  draw model 2D
1472        :param qmax: the maximum value to draw model 2D
1473        :param qstep: the number of division of Qx and Qy of the model to draw
1474           
1475        """
1476        if not enable2D:
1477            return None
1478        try:
1479            from model_thread import Calc2D
1480            ## If a thread is already started, stop it
1481            if (self.calc_2D is not None) and self.calc_2D.isrunning():
1482                self.calc_2D.stop()
1483            self.calc_2D = Calc2D(model=model, 
1484                                    data=data,
1485                                    page_id=page_id,
1486                                    smearer=smearer,
1487                                    qmin=qmin,
1488                                    qmax=qmax,
1489                                    toggle_mode_on=toggle_mode_on,
1490                                    state=state,
1491                                    completefn=self._complete2D,
1492                                    update_chisqr=update_chisqr)
1493            self.calc_2D.queue()
1494
1495        except:
1496            raise
1497            #msg = " Error occurred when drawing %s Model 2D: " % model.name
1498            #msg += " %s" % sys.exc_value
1499            #wx.PostEvent(self.parent, StatusEvent(status=msg))
1500
1501    def _draw_model1D(self, model, page_id, data, 
1502                      qmin, qmax, smearer=None,
1503                state=None,
1504                toggle_mode_on=False, update_chisqr=True, 
1505                enable1D=True):
1506        """
1507        Draw model 1D from loaded data1D
1508       
1509        :param data: loaded data
1510        :param model: the model to plot
1511       
1512        """
1513        if not enable1D:
1514            return 
1515        try:
1516            from model_thread import Calc1D
1517            ## If a thread is already started, stop it
1518            if (self.calc_1D is not None) and self.calc_1D.isrunning():
1519                self.calc_1D.stop()
1520            self.calc_1D = Calc1D(data=data,
1521                                  model=model,
1522                                  page_id=page_id, 
1523                                  qmin=qmin,
1524                                  qmax=qmax,
1525                                  smearer=smearer,
1526                                  state=state,
1527                                  toggle_mode_on=toggle_mode_on,
1528                                  completefn=self._complete1D,
1529                                  #updatefn = self._update1D,
1530                                  update_chisqr=update_chisqr)
1531            self.calc_1D.queue()
1532        except:
1533            msg = " Error occurred when drawing %s Model 1D: " % model.name
1534            msg += " %s" % sys.exc_value
1535            wx.PostEvent(self.parent, StatusEvent(status=msg))
1536   
1537 
1538   
1539    def _cal_chisqr(self, page_id, data, index=None): 
1540        """
1541        Get handy Chisqr using the output from draw1D and 2D,
1542        instead of calling expansive CalcChisqr in guithread
1543        """
1544        data_copy = deepcopy(data)
1545       
1546        # default chisqr
1547        chisqr = None
1548        #to compute chisq make sure data has valid data
1549        # return None if data == None
1550        if not check_data_validity(data_copy):
1551            return chisqr
1552       
1553        # Get data: data I, theory I, and data dI in order
1554        if data_copy.__class__.__name__ == "Data2D":
1555            if index == None: 
1556                index = numpy.ones(len(data_copy.data),ntype=bool)
1557            if self.weight != None:
1558                data_copy.err_data = self.weight
1559            # get rid of zero error points
1560            index = index & (data_copy.err_data != 0) 
1561            index = index & (numpy.isfinite(data_copy.data)) 
1562            fn = data_copy.data[index] 
1563            theory_data = self.page_finder[page_id].get_theory_data(fid=data_copy.id)
1564            gn = theory_data.data[index]
1565            en = data_copy.err_data[index]
1566        else:
1567            # 1 d theory from model_thread is only in the range of index
1568            if index == None: 
1569                index = numpy.ones(len(data_copy.y), ntype=bool)
1570            if self.weight != None:
1571                data_copy.dy = self.weight
1572            if data_copy.dy == None or data_copy.dy == []:
1573                dy = numpy.ones(len(data_copy.y))
1574            else:
1575                ## Set consitently w/AbstractFitengine:
1576                # But this should be corrected later.
1577                dy = deepcopy(data_copy.dy)
1578                dy[dy==0] = 1
1579            fn = data_copy.y[index] 
1580            theory_data = self.page_finder[page_id].get_theory_data(fid=data_copy.id)
1581            gn = theory_data.y
1582            en = dy[index]
1583        # residual
1584        res = (fn - gn) / en
1585        residuals = res[numpy.isfinite(res)]
1586        # get chisqr only w/finite
1587        chisqr = numpy.average(residuals * residuals)
1588        self._plot_residuals(page_id, data_copy, index)
1589       
1590        return chisqr
1591   
1592    def _plot_residuals(self, page_id, data=None, index=None): 
1593        """
1594        Plot the residuals
1595       
1596        :param data: data
1597        :param index: index array (bool)
1598        : Note: this is different from the residuals in cal_chisqr()
1599        """
1600        data_copy = deepcopy(data)
1601        # Get data: data I, theory I, and data dI in order
1602        if data_copy.__class__.__name__ == "Data2D":
1603            # build residuals
1604            residuals = Data2D()
1605            #residuals.copy_from_datainfo(data)
1606            # Not for trunk the line below, instead use the line above
1607            data_copy.clone_without_data(len(data_copy.data), residuals)
1608            residuals.data = None
1609            fn = data_copy.data#[index]
1610            theory_data = self.page_finder[page_id].get_theory_data(fid=data_copy.id)
1611            gn = theory_data.data#[index]
1612            en = self.weight#data_copy.err_data#[index]
1613            residuals.data = (fn - gn) / en
1614            residuals.qx_data = data_copy.qx_data#[index]
1615            residuals.qy_data = data_copy.qy_data #[index]
1616            residuals.q_data = data_copy.q_data#[index]
1617            residuals.err_data = numpy.ones(len(residuals.data))#[index]
1618            residuals.xmin = min(residuals.qx_data)
1619            residuals.xmax = max(residuals.qx_data)
1620            residuals.ymin = min(residuals.qy_data)
1621            residuals.ymax = max(residuals.qy_data)
1622            residuals.q_data = data_copy.q_data#[index]
1623            residuals.mask = data_copy.mask
1624            residuals.scale = 'linear'
1625            # check the lengths
1626            if len(residuals.data) != len(residuals.q_data):
1627                return
1628        else:
1629            # 1 d theory from model_thread is only in the range of index
1630            if data_copy.dy == None or data_copy.dy == []:
1631                dy = numpy.ones(len(data_copy.y))
1632            else:
1633                if self.weight == None:
1634                    dy = numpy.ones(len(data_copy.y))
1635                ## Set consitently w/AbstractFitengine:
1636                ## But this should be corrected later.
1637                else:
1638                    dy = self.weight#deepcopy(data_copy.dy)
1639                dy[dy==0] = 1 
1640            fn = data_copy.y[index] 
1641            theory_data = self.page_finder[page_id].get_theory_data(fid=data_copy.id)
1642            gn = theory_data.y
1643            en = dy[index]
1644            # build residuals
1645            residuals = Data1D()
1646            residuals.y = (fn - gn) / en
1647            residuals.x = data_copy.x[index]
1648            residuals.dy = numpy.ones(len(residuals.y))
1649            residuals.dx = None
1650            residuals.dxl = None
1651            residuals.dxw = None
1652            residuals.ytransform = 'y'
1653            # For latter scale changes
1654            residuals.xaxis('\\rm{Q} ', 'A^{-1}')
1655            residuals.yaxis('\\rm{Residuals} ', 'normalized')
1656        new_plot = residuals
1657        new_plot.name = "Residuals for " + str(data.name)
1658        ## allow to highlight data when plotted
1659        new_plot.interactive = True
1660        ## when 2 data have the same id override the 1 st plotted
1661        new_plot.id = "res" + str(data_copy.id)#name + " residuals"
1662        ##group_id specify on which panel to plot this data
1663        group_id = self.page_finder[page_id].get_graph_id()
1664        if group_id == None:
1665            group_id = data.group_id
1666        new_plot.group_id ="res" + str(group_id)
1667        #new_plot.is_data = True
1668        ##post data to plot
1669        title = new_plot.name
1670        # plot data
1671        wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot, title=title)) 
1672        #reset weight 
1673        self.weight = None
1674#def profile(fn, *args, **kw):
1675#    import cProfile, pstats, os
1676#    global call_result
1677#    def call():
1678#        global call_result
1679#        call_result = fn(*args, **kw)
1680#    cProfile.runctx('call()', dict(call=call), {}, 'profile.out')
1681#    stats = pstats.Stats('profile.out')
1682#    #stats.sort_stats('time')
1683#    stats.sort_stats('calls')
1684#    stats.print_stats()
1685#    os.unlink('profile.out')
1686#    return call_result
1687if __name__ == "__main__":
1688    i = Plugin()
1689   
1690   
1691   
1692   
Note: See TracBrowser for help on using the repository browser.