source: sasview/fittingview/src/sans/perspectives/fitting/fitting.py @ 92a5ac92

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

make sure that batch display right results

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