source: sasview/sansview/perspectives/fitting/fitting.py @ 67ae937

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

working on batch mode

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