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

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

Can change plotpanel 1D 2D title and set Graph as default: update datapanel cb and menu_graph accordingly

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