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

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

working on batch result

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