source: sasview/sansview/perspectives/fitting/fitting.py @ 5618f92

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

working on batch fit

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