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

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

added custom weighting for single fit

  • Property mode set to 100644
File size: 64.2 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, weight, 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(weight=weight, 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
701                    for fitproblem in  value.get_fit_problem():
702                        if sim_fitter is None:
703                            fitter = Fit(self._fit_engine) 
704                            self._fit_helper(fitproblem, pars, fitter, fit_id)
705                            fitter_list.append(fitter) 
706                        else:
707                            fitter = sim_fitter
708                            self._fit_helper(fitproblem, pars, fitter, fit_id)
709                        fit_id += 1
710                    list_page_id.append(page_id)
711                    current_page_id = page_id
712                    value.clear_model_param()
713            except:
714                raise
715                msg= "%s error: %s" % (engineType, sys.exc_value)
716                wx.PostEvent(self.parent, StatusEvent(status=msg, info="error",
717                                                      type="stop"))
718                return 
719        ## If a thread is already started, stop it
720        #if self.calc_fit!= None and self.calc_fit.isrunning():
721        #    self.calc_fit.stop()
722        msg = "Fitting is in progress..."
723        wx.PostEvent( self.parent, StatusEvent(status=msg, type="progress" ))
724       
725        #Handler used for park engine displayed message
726        handler = ConsoleUpdate(parent=self.parent,
727                                manager=self,
728                                improvement_delta=0.1)
729        self._mac_sleep(0.2)
730        ## perform single fit
731        if fitproblem_count == 1:
732            calc_fit = FitThread(handler = handler,
733                                    fn=fitter_list,
734                                    pars=pars,
735                                    page_id=list_page_id,
736                                    completefn=self._single_fit_completed,
737                                    ftol=self.ftol)
738        else:
739            current_page_id = self.sim_page.uid
740            ## Perform more than 1 fit at the time
741            calc_fit = FitThread(handler=handler,
742                                    fn=fitter_list,
743                                    page_id=list_page_id,
744                                    updatefn=handler.update_fit,
745                                    completefn=self._simul_fit_completed,
746                                    ftol=self.ftol)
747        self.fit_thread_list[current_page_id] = calc_fit
748        calc_fit.queue()
749        msg = "Fitting is in progress..."
750        wx.PostEvent( self.parent, StatusEvent(status=msg, type="progress" ))
751       
752        self.ready_fit(calc_fit=calc_fit)
753       
754    def ready_fit(self, calc_fit):
755        """
756        Ready for another fit
757        """
758        if self.fitproblem_count != None and self.fitproblem_count > 1:
759            calc_fit.ready(2.5)
760        else:
761            time.sleep(0.4)
762           
763    def remove_plot(self, uid, fid=None, theory=False):
764        """
765        remove model plot when a fit page is closed
766        :param uid: the id related to the fitpage to close
767        :param fid: the id of the fitproblem(data, model, range,etc)
768        """
769        if uid not in self.page_finder.keys():
770            return
771        fitproblemList = self.page_finder[uid].get_fit_problem(fid)
772        for fitproblem in fitproblemList:
773            data = fitproblem.get_fit_data()
774            model = fitproblem.get_model()
775            plot_id = None
776            if model is not None:
777                plot_id = data.id + name
778            if theory:
779                plot_id = data.id
780            group_id = data.group_id
781            wx.PostEvent(self.parent, NewPlotEvent(id=plot_id,
782                                                       group_id=group_id,
783                                                       action='remove'))
784           
785    def store_data(self, uid, data_list=None, caption=None):
786        """
787        Recieve a list of data and store them ans well as a caption of
788        the fit page where they come from.
789        :param uid: if related to a fit page
790        :param data_list: list of data to fit
791        :param caption: caption of the window related to these data
792        """
793        if data_list is None:
794            data_list = []
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             
851        self.store_data(uid=page.uid, data_list=page.get_data_list(), 
852                        caption=page.window_caption)
853        if self.sim_page is not None:
854            self.sim_page.draw_page()
855        return page
856           
857    def _onEVT_SLICER_PANEL(self, event):
858        """
859        receive and event telling to update a panel with a name starting with
860        event.panel_name. this method update slicer panel for a given interactor.
861       
862        :param event: contains type of slicer , paramaters for updating the panel
863            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                    continue
967                batch_result["Chi2"].append(res.fitness)
968                for index  in range(len(pars)):
969                    batch_result[pars[index]].append(res.pvec[index])
970                    item = res.stderr[index]
971                    batch_result["error on %s" % pars[index]].append(item)
972            pid = page_id[0]
973            self.page_finder[pid].set_result(result=batch_result)     
974            self.parent.on_set_batch_result(data=batch_result, 
975                                            name=self.sub_menu)
976            for uid in page_id:
977                cpage = self.fit_panel.get_page_by_id(uid)
978                cpage._on_fit_complete()
979           
980    def _single_fit_completed(self, result, pars, page_id, elapsed=None):
981        """
982         Display fit result on one page of the notebook.
983        :param result: list of object generated when fit ends
984        :param pars: list of names of parameters fitted
985        :param page_id: list of page ids which called fit function
986        :param elapsed: time spent at the fitting level
987        """ 
988        self._mac_sleep(0.2)
989        if page_id[0] in self.fit_thread_list.keys():
990            del self.fit_thread_list[page_id[0]] 
991        if self.batch_on:
992            wx.CallAfter(self._batch_single_fit_complete_helper,
993                          result, pars, page_id, elapsed=None)
994            return 
995        else: 
996            try:
997                result = result[0]
998                if result == None:
999                    self._update_fit_button(page_id)
1000                    msg= "Single Fitting did not converge!!!"
1001                    wx.PostEvent(self.parent, 
1002                                 StatusEvent(status=msg, 
1003                                             info="warning",
1004                                             type="stop"))
1005                    return
1006                if not numpy.isfinite(result.fitness) or \
1007                        numpy.any(result.pvec == None) or \
1008                        not numpy.all(numpy.isfinite(result.pvec)):
1009                    msg = "Single Fitting did not converge!!!"
1010                    wx.PostEvent(self.parent, 
1011                                 StatusEvent(status=msg, 
1012                                             info="warning",
1013                                             type="stop"))
1014                    self._update_fit_button(page_id)
1015                    return
1016               
1017                for uid in page_id:
1018                    cpage = self.fit_panel.get_page_by_id(uid)
1019                    # Make sure we got all results
1020                    #(CallAfter is important to MAC)
1021                    wx.CallAfter(cpage.onsetValues, result.fitness, pars, 
1022                                 result.pvec, result.stderr)
1023                    cpage._on_fit_complete()
1024                if result.stderr == None:
1025                    msg = "Fit Abort: "
1026                else:
1027                    msg = "Fitting: "
1028                msg += "Completed!!!"
1029                wx.PostEvent(self.parent, StatusEvent(status=msg))
1030            except ValueError:
1031                self._update_fit_button(page_id)
1032                msg = "Single Fitting did not converge!!!"
1033                wx.PostEvent(self.parent, StatusEvent(status=msg, info="error",
1034                                                      type="stop"))
1035            except:
1036                self._update_fit_button(page_id)
1037                msg = "Single Fit completed but Following"
1038                msg += " error occurred:%s" % sys.exc_value
1039                wx.PostEvent(self.parent, StatusEvent(status=msg, info="error",
1040                                                      type="stop"))
1041                raise
1042               
1043    def _simul_fit_completed(self, result, page_id, pars=None, elapsed=None):
1044        """
1045        Display result of the fit on related panel(s).
1046        :param result: list of object generated when fit ends
1047        :param pars: list of names of parameters fitted
1048        :param page_id: list of page ids which called fit function
1049        :param elapsed: time spent at the fitting level
1050        """
1051        result = result[0]
1052        self.fit_thread_list = {}
1053        if page_id is None:
1054            page_id = []
1055        ## fit more than 1 model at the same time
1056        self._mac_sleep(0.2) 
1057        try:
1058            msg = "" 
1059            if result == None:
1060                self._update_fit_button(page_id)
1061                msg= "Complex Fitting did not converge!!!"
1062                wx.PostEvent(self.parent, StatusEvent(status=msg,
1063                                                      type="stop"))
1064                return
1065            if not numpy.isfinite(result.fitness) or \
1066                numpy.any(result.pvec == None) or not \
1067                numpy.all(numpy.isfinite(result.pvec)):
1068                self._update_fit_button(page_id)
1069                msg= "Simultaneous Fitting did not converge!!!"
1070                wx.PostEvent(self.parent, StatusEvent(status=msg,type="stop"))
1071                return
1072             
1073            for uid in page_id:   
1074                fpdict = self.page_finder[uid]
1075                for value in fpdict.itervalues():
1076                    model = value.get_model()
1077                    data =  value.get_fit_data()
1078                    small_param_name = []
1079                    small_out = []
1080                    small_cov = []
1081                    #Separate result in to data corresponding to each page
1082                    for p in result.parameters:
1083                        model_name, param_name = self.split_string(p.name) 
1084                        if model.name == model_name:
1085                            p_name= model.name+"."+param_name
1086                            if p.name == p_name:     
1087                                if p.value != None and numpy.isfinite(p.value):
1088                                    small_out.append(p.value)
1089                                    small_param_name.append(param_name)
1090                                    small_cov.append(p.stderr)
1091                # Display result on each page
1092                cpage = self.fit_panel.get_page_by_id(uid)
1093                wx.CallAfter(cpage.onsetValues, 
1094                                    result.fitness,
1095                                  small_param_name,
1096                                  small_out,small_cov)
1097                cpage._on_fit_complete()
1098                msg = "Fit completed!"
1099                wx.PostEvent(self.parent, StatusEvent(status=msg))
1100        except Exception:
1101            raise
1102            self._update_fit_button(page_id)
1103            msg = "Complex Fitting did not converge!!!"
1104            wx.PostEvent(self.parent, StatusEvent(status=msg, info="error",
1105                                                  type="stop"))
1106            return
1107        except:
1108            self._update_fit_button(page_id)
1109            msg = "Simultaneous Fit completed"
1110            msg += " but Following error occurred:%s" % sys.exc_value
1111            wx.PostEvent(self.parent, StatusEvent(status=msg, type="stop"))
1112   
1113    def _update_fit_button(self, page_id):
1114        """
1115        Update Fit button when fit stopped
1116       
1117        : parameter page_id: fitpage where the button is
1118        """
1119        if page_id.__class__.__name__ != 'list':
1120            page_id = [page_id]
1121        for uid in page_id: 
1122            page = self.fit_panel.get_page_by_id(uid)
1123            page._on_fit_complete()
1124       
1125    def _on_show_panel(self, event):
1126        """
1127        """
1128        pass
1129       
1130    def _onset_engine_park(self,event):
1131        """
1132        set engine to park
1133        """
1134        self._on_change_engine('park')
1135       
1136    def _onset_engine_scipy(self,event):
1137        """
1138        set engine to scipy
1139        """
1140        self._on_change_engine('scipy')
1141       
1142    def _on_slicer_event(self, event):
1143        """
1144        Receive a panel as event and send it to guiframe
1145       
1146        :param event: event containing a panel
1147       
1148        """
1149        if event.panel is not None:
1150            new_panel = event.panel
1151            self.slicer_panels.append(event.panel)
1152            # Set group ID if available
1153            event_id = self.parent.popup_panel(new_panel)
1154            new_panel.uid = event_id
1155            self.mypanels.append(new_panel) 
1156       
1157    def _onclearslicer(self, event):
1158        """
1159        Clear the boxslicer when close the panel associate with this slicer
1160        """
1161        name =event.GetPane().caption
1162   
1163        for panel in self.slicer_panels:
1164            if panel.window_caption==name:
1165               
1166                for item in self.parent.panels:
1167                    if hasattr(self.parent.panels[item], "uid"):
1168                        if self.parent.panels[item].uid ==panel.base.uid:
1169                            self.parent.panels[item].onClearSlicer(event)
1170                            self.parent._mgr.Update()
1171                            break 
1172                break
1173   
1174    def _return_engine_type(self):
1175        """
1176        return the current type of engine
1177        """
1178        return self._fit_engine
1179     
1180     
1181    def _on_change_engine(self, engine='park'):
1182        """
1183        Allow to select the type of engine to perform fit
1184       
1185        :param engine: the key work of the engine
1186       
1187        """
1188        ## saving fit engine name
1189        self._fit_engine = engine
1190        ## change menu item state
1191        if engine == "park":
1192            self.menu1.FindItemById(self.park_id).Check(True)
1193            self.menu1.FindItemById(self.scipy_id).Check(False)
1194        else:
1195            self.menu1.FindItemById(self.park_id).Check(False)
1196            self.menu1.FindItemById(self.scipy_id).Check(True)
1197        ## post a message to status bar
1198        msg = "Engine set to: %s" % self._fit_engine
1199        wx.PostEvent(self.parent, 
1200                     StatusEvent(status=msg))
1201        ## send the current engine type to fitpanel
1202        self.fit_panel._on_engine_change(name=self._fit_engine)
1203
1204       
1205    def _on_model_panel(self, evt):
1206        """
1207        react to model selection on any combo box or model menu.plot the model 
1208       
1209        :param evt: wx.combobox event
1210       
1211        """
1212        model = evt.model
1213        uid = evt.uid
1214        qmin = evt.qmin
1215        qmax = evt.qmax
1216        smearer = evt.smearer
1217        caption = evt.caption
1218        enable_smearer = evt.enable_smearer
1219        if model == None:
1220            return
1221        if uid not in self.page_finder.keys():
1222            return
1223        # save the name containing the data name with the appropriate model
1224        self.page_finder[uid].set_model(model)
1225        self.page_finder[uid].enable_smearing(enable_smearer)
1226        self.page_finder[uid].set_range(qmin=qmin, qmax=qmax)
1227        self.page_finder[uid].set_fit_tab_caption(caption=caption)
1228        if self.sim_page is not None:
1229            self.sim_page.draw_page()
1230       
1231    def _update1D(self, x, output):
1232        """
1233        Update the output of plotting model 1D
1234        """
1235        msg = "Plot updating ... "
1236        wx.PostEvent(self.parent, StatusEvent(status=msg,type="update"))
1237       
1238    def _complete1D(self, x, y, page_id, elapsed, index, model,
1239                    toggle_mode_on=False, state=None, 
1240                    data=None, update_chisqr=True):
1241        """
1242        Complete plotting 1D data
1243        """ 
1244        try:
1245            new_plot = Data1D(x=x, y=y)
1246            new_plot.is_data = False
1247            new_plot.symbol = GUIFRAME_ID.CURVE_SYMBOL_NUM
1248            _yaxis, _yunit = data.get_yaxis() 
1249            _xaxis, _xunit = data.get_xaxis() 
1250            new_plot.title = data.name
1251            new_plot.group_id = data.group_id
1252            new_plot.id =  str(page_id) + "model"
1253            if new_plot.id in self.color_dict:
1254                new_plot.custom_color = self.color_dict[new_plot.id] 
1255            #find if this theory was already plotted and replace that plot given
1256            #the same id
1257            theory_data = self.page_finder[page_id].get_theory_data(fid=data.id)
1258            new_plot.name = model.name + " ["+ str(model.__class__.__name__)+"]"
1259            new_plot.xaxis(_xaxis, _xunit)
1260            new_plot.yaxis(_yaxis, _yunit)
1261            self.page_finder[page_id].set_theory_data(data=new_plot, fid=data.id)
1262            self.parent.update_theory(data_id=data.id, theory=new_plot,
1263                                       state=state)   
1264            current_pg = self.fit_panel.get_page_by_id(page_id)
1265            title = new_plot.title
1266            wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot,
1267                                            title=str(title)))
1268            caption = current_pg.window_caption
1269            self.page_finder[page_id].set_fit_tab_caption(caption=caption)
1270            self.page_finder[page_id].set_theory_data(data=new_plot, 
1271                                                      fid=data.id)
1272            if toggle_mode_on:
1273                wx.PostEvent(self.parent, 
1274                             NewPlotEvent(group_id=str(page_id) + " Model2D",
1275                                               action="Hide"))
1276            else:
1277                if update_chisqr:
1278                    wx.PostEvent(current_pg,
1279                                 Chi2UpdateEvent(output=self._cal_chisqr(data=data,
1280                                                            page_id=page_id,
1281                                                            index=index)))
1282                else:
1283                    self._plot_residuals(page_id, data, index)
1284
1285            msg = "Computation  completed!"
1286            wx.PostEvent( self.parent, StatusEvent(status=msg, type="stop" ))
1287        except:
1288            raise
1289            #msg = " Error occurred when drawing %s Model 1D: " % new_plot.name
1290            #msg += " %s"  % sys.exc_value
1291            #wx.PostEvent(self.parent, StatusEvent(status=msg, type="stop"))
1292   
1293    def _update2D(self, output,time=None):
1294        """
1295        Update the output of plotting model
1296        """
1297        wx.PostEvent(self.parent, StatusEvent(status="Plot \
1298        #updating ... ", type="update"))
1299        #self.ready_fit()
1300 
1301    def _complete2D(self, image, data, model, page_id,  elapsed, index, qmin,
1302                     qmax, toggle_mode_on=False, state=None, 
1303                     update_chisqr=True):
1304        """
1305        Complete get the result of modelthread and create model 2D
1306        that can be plot.
1307        """
1308        new_plot= Data2D(image=image, err_image=data.err_data)
1309        new_plot.name = model.name
1310        new_plot.title = "Analytical model 2D "
1311        new_plot.id = str(page_id) + "model"
1312        new_plot.group_id = str(page_id) + " Model2D"
1313        new_plot.detector = data.detector
1314        new_plot.source = data.source
1315        new_plot.is_data = False 
1316        new_plot.qx_data = data.qx_data
1317        new_plot.qy_data = data.qy_data
1318        new_plot.q_data = data.q_data
1319        new_plot.mask = data.mask
1320        ## plot boundaries
1321        new_plot.ymin = data.ymin
1322        new_plot.ymax = data.ymax
1323        new_plot.xmin = data.xmin
1324        new_plot.xmax = data.xmax
1325        title = data.title
1326        if len(title) > 1:
1327            new_plot.title = "Model2D for " + data.name
1328        new_plot.is_data = False
1329        new_plot.name = model.name + " [" + \
1330                                    str(model.__class__.__name__) + "-2D]"
1331        theory_data = deepcopy(new_plot)
1332        theory_data.name = "Unknown"
1333       
1334        self.page_finder[page_id].set_theory_data(data=theory_data, fid=data.id)
1335        self.parent.update_theory(data_id=data.id, 
1336                                       theory=new_plot,
1337                                       state=state) 
1338        current_pg = self.fit_panel.get_page_by_id(page_id)
1339        title = new_plot.title
1340        wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot,
1341                                               title=title))
1342        self.page_finder[page_id].set_theory_data(data=new_plot, fid=data.id)
1343        if toggle_mode_on:
1344            wx.PostEvent(self.parent, 
1345                             NewPlotEvent(group_id=str(page_id) + " Model1D",
1346                                               action="Hide"))
1347        else:
1348            # Chisqr in fitpage
1349            if update_chisqr:
1350                wx.PostEvent(current_pg,
1351                             Chi2UpdateEvent(output=self._cal_chisqr(data=data,
1352                                                         page_id=page_id,
1353                                                         index=index)))
1354            else:
1355                self._plot_residuals(page_id, data, index)
1356        msg = "Computation  completed!"
1357        wx.PostEvent(self.parent, StatusEvent(status=msg, type="stop"))
1358   
1359    def _draw_model2D(self, model, page_id, qmin,
1360                      qmax,
1361                      data=None, smearer=None,
1362                      description=None, enable2D=False,
1363                      state=None,
1364                      toggle_mode_on=False,
1365                       update_chisqr=True):
1366        """
1367        draw model in 2D
1368       
1369        :param model: instance of the model to draw
1370        :param description: the description of the model
1371        :param enable2D: when True allows to draw model 2D
1372        :param qmin: the minimum value to  draw model 2D
1373        :param qmax: the maximum value to draw model 2D
1374        :param qstep: the number of division of Qx and Qy of the model to draw
1375           
1376        """
1377        if not enable2D:
1378            return None
1379        try:
1380            from model_thread import Calc2D
1381            ## If a thread is already started, stop it
1382            if (self.calc_2D is not None) and self.calc_2D.isrunning():
1383                self.calc_2D.stop()
1384            self.calc_2D = Calc2D(model=model, 
1385                                    data=data,
1386                                    page_id=page_id,
1387                                    smearer=smearer,
1388                                    qmin=qmin,
1389                                    qmax=qmax,
1390                                    toggle_mode_on=toggle_mode_on,
1391                                    state=state,
1392                                    completefn=self._complete2D,
1393                                    update_chisqr=update_chisqr)
1394            self.calc_2D.queue()
1395
1396        except:
1397            raise
1398            #msg = " Error occurred when drawing %s Model 2D: " % model.name
1399            #msg += " %s" % sys.exc_value
1400            #wx.PostEvent(self.parent, StatusEvent(status=msg))
1401
1402    def _draw_model1D(self, model, page_id, data, 
1403                      qmin, qmax, smearer=None,
1404                state=None,
1405                toggle_mode_on=False, update_chisqr=True, 
1406                enable1D=True):
1407        """
1408        Draw model 1D from loaded data1D
1409       
1410        :param data: loaded data
1411        :param model: the model to plot
1412       
1413        """
1414        if not enable1D:
1415            return 
1416        try:
1417            from model_thread import Calc1D
1418            ## If a thread is already started, stop it
1419            if (self.calc_1D is not None) and self.calc_1D.isrunning():
1420                self.calc_1D.stop()
1421            self.calc_1D = Calc1D(data=data,
1422                                  model=model,
1423                                  page_id=page_id, 
1424                                  qmin=qmin,
1425                                  qmax=qmax,
1426                                  smearer=smearer,
1427                                  state=state,
1428                                  toggle_mode_on=toggle_mode_on,
1429                                  completefn=self._complete1D,
1430                                  #updatefn = self._update1D,
1431                                  update_chisqr=update_chisqr)
1432            self.calc_1D.queue()
1433        except:
1434            msg = " Error occurred when drawing %s Model 1D: " % model.name
1435            msg += " %s" % sys.exc_value
1436            wx.PostEvent(self.parent, StatusEvent(status=msg))
1437   
1438 
1439   
1440    def _cal_chisqr(self, page_id, data, index=None): 
1441        """
1442        Get handy Chisqr using the output from draw1D and 2D,
1443        instead of calling expansive CalcChisqr in guithread
1444        """
1445        data = deepcopy(data)
1446       
1447        # default chisqr
1448        chisqr = None
1449        #to compute chisq make sure data has valid data
1450        # return None if data == None
1451        if not check_data_validity(data):
1452            return chisqr
1453       
1454        # Get data: data I, theory I, and data dI in order
1455        if data.__class__.__name__ == "Data2D":
1456            if index == None: 
1457                index = numpy.ones(len(data.data),ntype=bool)
1458            if self.weight != None:
1459                data.err_data = self.weight
1460            # get rid of zero error points
1461            index = index & (data.err_data != 0) 
1462            index = index & (numpy.isfinite(data.data)) 
1463            fn = data.data[index] 
1464            theory_data = self.page_finder[page_id].get_theory_data(fid=data.id)
1465            gn = theory_data.data[index]
1466            en = data.err_data[index]
1467        else:
1468            # 1 d theory from model_thread is only in the range of index
1469            if index == None: 
1470                index = numpy.ones(len(data.y), ntype=bool)
1471            if self.weight != None:
1472                data.dy = self.weight
1473            if data.dy == None or data.dy == []:
1474                dy = numpy.ones(len(data.y))
1475            else:
1476                ## Set consitently w/AbstractFitengine:
1477                # But this should be corrected later.
1478                dy = deepcopy(data.dy)
1479                dy[dy==0] = 1 
1480            fn = data.y[index] 
1481            theory_data = self.page_finder[page_id].get_theory_data(fid=data.id)
1482            gn = theory_data.y
1483            en = dy[index]
1484        # residual
1485        res = (fn - gn) / en
1486        residuals = res[numpy.isfinite(res)]
1487        # get chisqr only w/finite
1488        chisqr = numpy.average(residuals * residuals)
1489        self._plot_residuals(page_id, data, index)
1490        #reset weight
1491        self.weight = None
1492        return chisqr
1493   
1494    def _plot_residuals(self, page_id, data=None, index=None): 
1495        """
1496        Plot the residuals
1497       
1498        :param data: data
1499        :param index: index array (bool)
1500        : Note: this is different from the residuals in cal_chisqr()
1501        """
1502        # Get data: data I, theory I, and data dI in order
1503        if data.__class__.__name__ == "Data2D":
1504            # build residuals
1505            residuals = Data2D()
1506            #residuals.copy_from_datainfo(data)
1507            # Not for trunk the line below, instead use the line above
1508            data.clone_without_data(len(data.data), residuals)
1509            residuals.data = None
1510            fn = data.data#[index]
1511            theory_data = self.page_finder[page_id].get_theory_data(fid=data.id)
1512            gn = theory_data.data#[index]
1513            en = data.err_data#[index]
1514            residuals.data = (fn - gn) / en
1515            residuals.qx_data = data.qx_data#[index]
1516            residuals.qy_data = data.qy_data #[index]
1517            residuals.q_data = data.q_data#[index]
1518            residuals.err_data = numpy.ones(len(residuals.data))#[index]
1519            residuals.xmin = min(residuals.qx_data)
1520            residuals.xmax = max(residuals.qx_data)
1521            residuals.ymin = min(residuals.qy_data)
1522            residuals.ymax = max(residuals.qy_data)
1523            residuals.q_data = data.q_data#[index]
1524            residuals.mask = data.mask
1525            residuals.scale = 'linear'
1526            # check the lengths
1527            if len(residuals.data) != len(residuals.q_data):
1528                return
1529        else:
1530            # 1 d theory from model_thread is only in the range of index
1531            if data.dy == None or data.dy == []:
1532                dy = numpy.ones(len(data.y))
1533            else:
1534                ## Set consitently w/AbstractFitengine:
1535                ## But this should be corrected later.
1536                dy = deepcopy(data.dy)
1537                dy[dy==0] = 1 
1538            fn = data.y[index] 
1539            theory_data = self.page_finder[page_id].get_theory_data(fid=data.id)
1540            gn = theory_data.y
1541            en = dy[index]
1542            # build residuals
1543            residuals = Data1D()
1544            residuals.y = (fn - gn) / en
1545            residuals.x = data.x[index]
1546            residuals.dy = numpy.ones(len(residuals.y))
1547            residuals.dx = None
1548            residuals.dxl = None
1549            residuals.dxw = None
1550            residuals.ytransform = 'y'
1551            # For latter scale changes
1552            residuals.xaxis('\\rm{Q} ', 'A^{-1}')
1553            residuals.yaxis('\\rm{Residuals} ', 'normalized')
1554        new_plot = residuals
1555        new_plot.name = "Residuals for " + str(data.name)
1556        ## allow to highlight data when plotted
1557        new_plot.interactive = True
1558        ## when 2 data have the same id override the 1 st plotted
1559        new_plot.id = new_plot.name#name + " residuals"
1560        ##group_id specify on which panel to plot this data
1561        new_plot.group_id = new_plot.id
1562        #new_plot.is_data = True
1563        ##post data to plot
1564        title = new_plot.name
1565       
1566        # plot data
1567        wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot, title=title))   
1568       
1569#def profile(fn, *args, **kw):
1570#    import cProfile, pstats, os
1571#    global call_result
1572#    def call():
1573#        global call_result
1574#        call_result = fn(*args, **kw)
1575#    cProfile.runctx('call()', dict(call=call), {}, 'profile.out')
1576#    stats = pstats.Stats('profile.out')
1577#    #stats.sort_stats('time')
1578#    stats.sort_stats('calls')
1579#    stats.print_stats()
1580#    os.unlink('profile.out')
1581#    return call_result
1582if __name__ == "__main__":
1583    i = Plugin()
1584   
1585   
1586   
1587   
Note: See TracBrowser for help on using the repository browser.