source: sasview/sansview/perspectives/fitting/fitting.py @ 6accb37

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

more print and return statement

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