source: sasview/fittingview/src/sans/perspectives/fitting/fitting.py @ 4c2c93f

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

think fixed the unexpected change in color when select for fitting from the context menu

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