source: sasview/sansview/perspectives/fitting/fitting.py @ dd8ee14

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 dd8ee14 was 1b1bbf9, checked in by Jae Cho <jhjcho@…>, 14 years ago

focus color (plots) and adjust panel sizes a bit

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