source: sasview/fittingview/src/sans/perspectives/fitting/fitting.py @ 706667b

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 706667b was 34a60dd, checked in by Gervaise Alina <gervyh@…>, 13 years ago

fit result correct but improperly displayed

  • Property mode set to 100644
File size: 75.4 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(BatchDrawEvent, EVT_BATCH_DRAW)   = wx.lib.newevent.NewEvent()
49
50if sys.platform.count("darwin")==0:
51    ON_MAC = False
52else:
53    ON_MAC = True   
54
55class Plugin(PluginBase):
56    """
57    Fitting plugin is used to perform fit
58    """
59    def __init__(self, standalone=False):
60        PluginBase.__init__(self, name="Fitting", standalone=standalone)
61       
62        #list of panel to send to guiframe
63        self.mypanels = []
64        # reference to the current running thread
65        self.calc_2D = None
66        self.calc_1D = None
67       
68        self.color_dict = {}
69       
70        self.fit_thread_list = {}
71        self.residuals = None
72        self.weight = None
73        self.fit_panel = None
74        # Start with a good default
75        self.elapsed = 0.022
76        # the type of optimizer selected, park or scipy
77        self.fitter  = None
78        self.fit_panel = None
79        #let fit ready
80        self.fitproblem_count = None
81        #Flag to let the plug-in know that it is running stand alone
82        self.standalone = True
83        ## dictionary of page closed and id
84        self.closed_page_dict = {}
85        ## Fit engine
86        self._fit_engine = 'scipy'
87        ## Relative error desired in the sum of squares (float); scipy only
88        self.ftol = SANS_F_TOL
89        #List of selected data
90        self.selected_data_list = []
91        ## list of slicer panel created to display slicer parameters and results
92        self.slicer_panels = []
93        # model 2D view
94        self.model2D_id = None
95        #keep reference of the simultaneous fit page
96        self.sim_page = None
97        self.index_model = 0
98        self.test_model_color = None
99        #Create a reader for fit page's state
100        self.state_reader = None 
101        self._extensions = '.fitv'
102        self.scipy_id = wx.NewId()
103        self.park_id = wx.NewId()
104       
105        self.temp_state = []
106        self.state_index = 0
107        self.sfile_ext = None
108        # take care of saving  data, model and page associated with each other
109        self.page_finder = {}
110        # Log startup
111        logging.info("Fitting plug-in started") 
112   
113    def create_fit_problem(self, page_id):
114        """
115        Given an ID create a fitproblem container
116        """
117        self.page_finder[page_id] = FitProblemDictionary()
118       
119    def delete_fit_problem(self, page_id):
120        """
121        Given an ID create a fitproblem container
122        """
123        if page_id in self.page_finder.iterkeys():
124            del self.page_finder[page_id]
125       
126    def add_color(self, color, id):
127        """
128        adds a color as a key with a plot id as its value to a dictionary
129        """
130        self.color_dict[id] = color
131       
132    def on_batch_selection(self, flag):
133        """
134        switch the the notebook of batch mode or not
135        """
136        self.batch_on = flag
137        if self.fit_panel is not None:
138            self.fit_panel.batch_on = self.batch_on
139       
140    def populate_menu(self, owner):
141        """
142        Create a menu for the Fitting plug-in
143       
144        :param id: id to create a menu
145        :param owner: owner of menu
146       
147        :return: list of information to populate the main menu
148       
149        """
150        #Menu for fitting
151        self.menu1 = wx.Menu()
152        id1 = wx.NewId()
153        simul_help = "Add new fit panel"
154        self.menu1.Append(id1, '&New Fit Page',simul_help)
155        wx.EVT_MENU(owner, id1, self.on_add_new_page)
156        self.menu1.AppendSeparator()
157        id1 = wx.NewId()
158        simul_help = "Simultaneous Fit"
159        self.menu1.Append(id1, '&Simultaneous Fit',simul_help)
160        wx.EVT_MENU(owner, id1, self.on_add_sim_page)
161        self.menu1.AppendSeparator()
162        #Set park engine
163       
164        scipy_help= "Scipy Engine: Perform Simple fit. More in Help window...."
165        self.menu1.AppendCheckItem(self.scipy_id, "Simple FitEngine [LeastSq]",
166                                   scipy_help) 
167        wx.EVT_MENU(owner, self.scipy_id,  self._onset_engine_scipy)
168       
169        park_help = "Park Engine: Perform Complex fit. More in Help window...."
170        self.menu1.AppendCheckItem(self.park_id, "Complex FitEngine [ParkMC]",
171                                   park_help) 
172        wx.EVT_MENU(owner, self.park_id,  self._onset_engine_park)
173       
174        self.menu1.FindItemById(self.scipy_id).Check(True)
175        self.menu1.FindItemById(self.park_id).Check(False)
176        self.menu1.AppendSeparator()
177        self.id_tol = wx.NewId()
178        ftol_help = "Change the current FTolerance (=%s) " % str(self.ftol)
179        ftol_help += "of Simple FitEngine..." 
180        self.menu1.Append(self.id_tol, "Change FTolerance [LeastSq Only]", 
181                                   ftol_help) 
182        wx.EVT_MENU(owner, self.id_tol,  self.show_ftol_dialog)
183       
184       
185        #create  menubar items
186        return [(self.menu1, self.sub_menu)]
187               
188    def on_add_sim_page(self, event):
189        """
190        Create a page to access simultaneous fit option
191        """
192        if self.sim_page != None:
193            self.sim_page.Show(True)
194            self.sim_page.Refresh()
195            self.sim_page.SetFocus()
196            self.parent._mgr.Update()
197            msg= "Simultaneous Fit page already opened"
198            wx.PostEvent(self.parent, StatusEvent(status= msg))
199            return 
200       
201        self.sim_page= self.fit_panel.add_sim_page()
202       
203    def help(self, evt):
204        """
205        Show a general help dialog.
206        """
207        from help_panel import  HelpWindow
208        frame = HelpWindow(None, -1, 'HelpWindow')   
209        frame.Show(True)
210       
211    def get_context_menu(self, plotpanel=None):
212        """
213        Get the context menu items available for P(r).them allow fitting option
214        for Data2D and Data1D only.
215       
216        :param graph: the Graph object to which we attach the context menu
217       
218        :return: a list of menu items with call-back function
219       
220        :note: if Data1D was generated from Theory1D 
221                the fitting option is not allowed
222               
223        """
224        graph = plotpanel.graph
225        fit_option = "Select data for fitting"
226        fit_hint =  "Dialog with fitting parameters "
227       
228        if graph.selected_plottable not in plotpanel.plots:
229            return []
230        item = plotpanel.plots[graph.selected_plottable]
231        if item.__class__.__name__ is "Data2D": 
232            if hasattr(item,"is_data"):
233                if item.is_data:
234                    return [[fit_option, fit_hint, self._onSelect]]
235                else:
236                    return [] 
237            return [[fit_option, fit_hint, self._onSelect]]
238        else:
239           
240            # if is_data is true , this in an actual data loaded
241            #else it is a data created from a theory model
242            if hasattr(item,"is_data"):
243                if item.is_data:
244                    return [[fit_option, fit_hint,
245                              self._onSelect]]
246                else:
247                    return [] 
248        return []   
249
250
251    def get_panels(self, parent):
252        """
253        Create and return a list of panel objects
254        """
255        self.parent = parent
256        #self.parent.Bind(EVT_FITSTATE_UPDATE, self.on_set_state_helper)
257        # Creation of the fit panel
258        self.fit_panel = FitPanel(parent=self.parent, manager=self)
259        self.on_add_new_page(event=None)
260        #Set the manager for the main panel
261        self.fit_panel.set_manager(self)
262        # List of windows used for the perspective
263        self.perspective = []
264        self.perspective.append(self.fit_panel.window_name)
265       
266        #index number to create random model name
267        self.index_model = 0
268        self.index_theory= 0
269        self.parent.Bind(EVT_SLICER_PANEL, self._on_slicer_event)
270        self.parent.Bind(EVT_SLICER_PARS_UPDATE, self._onEVT_SLICER_PANEL)
271        self.parent.Bind(EVT_BATCH_DRAW, self.on_display_grid)
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):
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        else:     
678            ## draw model 2D with no initial data
679            self._draw_model2D(model=model,
680                                page_id=page_id,
681                                data=data,
682                                enable2D=enable2D,
683                                smearer=smearer,
684                                qmin=qmin,
685                                qmax=qmax,
686                                fid=fid,
687                                weight=weight,
688                                state=state,
689                                toggle_mode_on=toggle_mode_on,
690                                update_chisqr=update_chisqr)
691           
692    def onFit(self, uid):
693        """
694        Get series of data, model, associates parameters and range and send then
695        to  series of fit engines. Fit data and model, display result to
696        corresponding panels.
697        :param uid: id related to the panel currently calling this fit function.
698        """
699        ##  count the number of fitproblem schedule to fit
700        fitproblem_count = 0
701        for value in self.page_finder.values():
702            if value.get_scheduled() == 1:
703                fitproblem_count += 1
704        ## if simultaneous fit change automatically the engine to park
705        if fitproblem_count > 1:
706            self._on_change_engine(engine='park')
707        self.fitproblem_count = fitproblem_count 
708        if self._fit_engine == "park":
709            engineType = "Simultaneous Fit"
710        else:
711            engineType = "Single Fit"
712        fitter_list = []       
713        sim_fitter = None     
714        is_single_fit = True
715        if self.sim_page is not None and self.sim_page.uid == uid:
716            #simulatanous fit only one engine need to be created
717            ## if simultaneous fit change automatically the engine to park
718            self._on_change_engine(engine='park')   
719            sim_fitter = Fit(self._fit_engine) 
720            fitter_list.append(sim_fitter) 
721            is_single_fit = False
722
723        self.fitproblem_count = fitproblem_count 
724        if self._fit_engine == "park":
725            engineType = "Simultaneous Fit"
726        else:
727            engineType = "Single Fit"
728       
729        self.current_pg = None
730        list_page_id = []
731        fit_id = 0
732        batch_inputs = {}
733        batch_outputs = {}
734        for page_id, value in self.page_finder.iteritems():
735            # For simulfit (uid give with None), do for-loop
736            # if uid is specified (singlefit), do it only on the page.
737            if engineType == "Single Fit":
738                if page_id != uid:
739                    continue
740            try:
741                if value.get_scheduled() == 1:
742                    value.nbr_residuals_computed = 0
743                    #Get list of parameters name to fit
744                    pars = []
745                    templist = []
746                    page = self.fit_panel.get_page_by_id(page_id)
747                    self.set_fit_weight(uid=page.uid, 
748                                     flag=page.get_weight_flag(),
749                                     is2d = page._is_2D())
750                    templist = page.get_param_list()
751                    for element in templist:
752                        name = str(element[1])
753                        pars.append(name)
754                    fitproblem_list = value.values()
755                    fitproblem_list.sort()
756                    for fitproblem in  fitproblem_list:
757                        if sim_fitter is None:
758                            fitter = Fit(self._fit_engine) 
759                            self._fit_helper(fitproblem=fitproblem, 
760                                                pars=pars, 
761                                                fitter=fitter,
762                                              fit_id=fit_id, 
763                                              batch_inputs=batch_inputs,
764                                              batch_outputs=batch_outputs)
765                            fitter_list.append(fitter) 
766                        else:
767                            fitter = sim_fitter
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                        fit_id += 1
775                    list_page_id.append(page_id)
776                    current_page_id = page_id
777                    value.clear_model_param()
778            except:
779                raise
780                msg= "%s error: %s" % (engineType, sys.exc_value)
781                wx.PostEvent(self.parent, StatusEvent(status=msg, info="error",
782                                                      type="stop"))
783                return 
784        ## If a thread is already started, stop it
785        #if self.calc_fit!= None and self.calc_fit.isrunning():
786        #    self.calc_fit.stop()
787        msg = "Fitting is in progress..."
788        wx.PostEvent( self.parent, StatusEvent(status=msg, type="progress" ))
789       
790        #Handler used for park engine displayed message
791        handler = ConsoleUpdate(parent=self.parent,
792                                manager=self,
793                                improvement_delta=0.1)
794        self._mac_sleep(0.2)
795        ## perform single fit
796        if is_single_fit:
797            calc_fit = FitThread(handler = handler,
798                                    fn=fitter_list,
799                                    pars=pars,
800                                    batch_inputs=batch_inputs,
801                                    batch_outputs=batch_outputs,
802                                    page_id=list_page_id,
803                                    completefn=self._single_fit_completed,
804                                    ftol=self.ftol)
805        else:
806            current_page_id = self.sim_page.uid
807            ## Perform more than 1 fit at the time
808            calc_fit = FitThread(handler=handler,
809                                    fn=fitter_list,
810                                    batch_inputs=batch_inputs,
811                                    batch_outputs=batch_outputs,
812                                    page_id=list_page_id,
813                                    updatefn=handler.update_fit,
814                                    completefn=self._simul_fit_completed,
815                                    ftol=self.ftol)
816        self.fit_thread_list[current_page_id] = calc_fit
817        calc_fit.queue()
818        msg = "Fitting is in progress..."
819        wx.PostEvent( self.parent, StatusEvent(status=msg, type="progress" ))
820       
821        self.ready_fit(calc_fit=calc_fit)
822       
823    def ready_fit(self, calc_fit):
824        """
825        Ready for another fit
826        """
827        if self.fitproblem_count != None and self.fitproblem_count > 1:
828            calc_fit.ready(2.5)
829        else:
830            time.sleep(0.4)
831           
832    def remove_plot(self, uid, fid=None, theory=False):
833        """
834        remove model plot when a fit page is closed
835        :param uid: the id related to the fitpage to close
836        :param fid: the id of the fitproblem(data, model, range,etc)
837        """
838        if uid not in self.page_finder.keys():
839            return
840        fitproblemList = self.page_finder[uid].get_fit_problem(fid)
841        for fitproblem in fitproblemList:
842            data = fitproblem.get_fit_data()
843            model = fitproblem.get_model()
844            plot_id = None
845            if model is not None:
846                plot_id = data.id + name
847            if theory:
848                plot_id = data.id
849            group_id = data.group_id
850            wx.PostEvent(self.parent, NewPlotEvent(id=plot_id,
851                                                       group_id=group_id,
852                                                       action='remove'))
853           
854    def store_data(self, uid, data_list=None, caption=None):
855        """
856        Recieve a list of data and store them ans well as a caption of
857        the fit page where they come from.
858        :param uid: if related to a fit page
859        :param data_list: list of data to fit
860        :param caption: caption of the window related to these data
861        """
862        if data_list is None:
863            data_list = []
864       
865        self.page_finder[uid].set_fit_data(data=data_list)
866        if caption is not None:
867            self.page_finder[uid].set_fit_tab_caption(caption=caption)
868           
869    def on_add_new_page(self, event=None):
870        """
871        ask fit panel to create a new empty page
872        """
873        try:
874            page = self.fit_panel.add_empty_page()
875            page_caption = page.window_caption
876            # add data associated to the page created
877            if page != None: 
878                wx.PostEvent(self.parent, StatusEvent(status="Page Created",
879                                               info="info"))
880            else:
881                msg = "Page was already Created"
882                wx.PostEvent(self.parent, StatusEvent(status=msg,
883                                                       info="warning"))
884            self.set_top_panel()
885        except:
886            msg = "Creating Fit page: %s"%sys.exc_value
887            wx.PostEvent(self.parent, StatusEvent(status=msg, info="error"))
888       
889    def add_fit_page(self, data):
890        """
891        given a data, ask to the fitting panel to create a new fitting page,
892        get this page and store it into the page_finder of this plug-in
893        :param data: is a list of data
894        """
895        page = self.fit_panel.set_data(data)
896        # page could be None when loading state files
897        if page == None:
898            return page
899        page_caption = page.window_caption
900        #append Data1D to the panel containing its theory
901        #if theory already plotted
902        if page.uid in self.page_finder:
903            data = page.get_data()
904            theory_data = self.page_finder[page.uid].get_theory_data(data.id)
905            if issubclass(data.__class__, Data2D):
906                data.group_id = wx.NewId()
907                if theory_data is not None:
908                    group_id = str(page.uid) + " Model1D"
909                    wx.PostEvent(self.parent, 
910                             NewPlotEvent(group_id=group_id,
911                                               action="delete"))
912                    self.parent.update_data(prev_data=theory_data,
913                                             new_data=data)     
914            else:
915                if theory_data is not None:
916                    group_id = str(page.uid) + " Model2D"
917                    data.group_id = theory_data.group_id
918                    wx.PostEvent(self.parent, 
919                             NewPlotEvent(group_id=group_id,
920                                               action="delete"))
921                    self.parent.update_data(prev_data=theory_data,
922                                             new_data=data)   
923        self.store_data(uid=page.uid, data_list=page.get_data_list(), 
924                        caption=page.window_caption)
925        if self.sim_page is not None and not self.batch_on:
926            self.sim_page.draw_page()
927        return page
928           
929    def _onEVT_SLICER_PANEL(self, event):
930        """
931        receive and event telling to update a panel with a name starting with
932        event.panel_name. this method update slicer panel
933        for a given interactor.
934       
935        :param event: contains type of slicer , paramaters for updating
936            the panel and panel_name to find the slicer 's panel concerned.
937        """
938        for item in self.parent.panels:
939            name = event.panel_name
940            if self.parent.panels[item].window_caption.startswith(name):
941                self.parent.panels[item].set_slicer(event.type, event.params)
942               
943        self.parent._mgr.Update()
944   
945    def _closed_fitpage(self, event):   
946        """
947        request fitpanel to close a given page when its unique data is removed
948        from the plot. close fitpage only when the a loaded data is removed
949        """   
950        if event is None or event.data is None:
951            return
952        if hasattr(event.data,"is_data"):
953            if not event.data.is_data or \
954                event.data.__class__.__name__ == "Data1D":
955                self.fit_panel.close_page_with_data(event.data) 
956 
957    def _reset_schedule_problem(self, value=0, uid=None):
958        """
959        unschedule or schedule all fitproblem to be fit
960        """
961        # case that uid is not specified
962        if uid == None:
963            for page_id in self.page_finder.keys():
964                self.page_finder[page_id].schedule_tofit(value)
965        # when uid is given
966        else:
967            if uid in self.page_finder.keys():
968                self.page_finder[uid].schedule_tofit(value)
969               
970    def _fit_helper(self, fitproblem, pars, fitter, fit_id,
971                    batch_inputs, batch_outputs):
972        """
973        Create and set fit engine with series of data and model
974        :param pars: list of fittable parameters
975        :param fitter_list: list of fit engine
976        :param value:  structure storing data mapped to their model, range etc..
977        """
978        data = fitproblem.get_fit_data()
979        model = fitproblem.get_model()
980        smearer = fitproblem.get_smearer()
981        qmin, qmax = fitproblem.get_range()
982
983        #Extra list of parameters and their constraints
984        listOfConstraint = []
985        param = fitproblem.get_model_param()
986        if len(param) > 0:
987            for item in param:
988                ## check if constraint
989                if item[0] != None and item[1] != None:
990                    listOfConstraint.append((item[0],item[1]))
991        new_model = model#deepcopy(model)
992        fitter.set_model(new_model, fit_id, pars, constraints=listOfConstraint)
993        fitter.set_data(data=data, id=fit_id, smearer=smearer, qmin=qmin, 
994                        qmax=qmax)
995        fitter.select_problem_for_fit(id=fit_id, value=1)
996       
997   
998    def _onSelect(self,event):
999        """
1000        when Select data to fit a new page is created .Its reference is
1001        added to self.page_finder
1002        """
1003        self.panel = event.GetEventObject()
1004        Plugin.on_perspective(self, event=event)
1005        self.select_data(self.panel)
1006       
1007    def select_data(self, panel):
1008        """
1009        """
1010        self.panel = panel
1011        for plottable in self.panel.graph.plottables:
1012            if plottable.__class__.__name__ in ["Data1D", "Theory1D"]:
1013                data_id = self.panel.graph.selected_plottable
1014                if plottable == self.panel.plots[data_id]:
1015                    data = plottable
1016                    self.add_fit_page(data=[data])
1017                    return
1018            else:
1019                data = plottable
1020                self.add_fit_page(data=[data])
1021        self.set_top_panel()
1022           
1023    def update_fit(self, result=None, msg=""):
1024        """
1025        """
1026        print "update_fit result", result
1027       
1028    def _batch_single_fit_complete_helper(self, result, pars, page_id, 
1029                            batch_outputs, batch_inputs, elapsed=None):
1030        """
1031        Display fit result in batch
1032        :param result: list of objects received fromt fit engines
1033        :param pars: list of  fitted parameters names
1034        :param page_id: list of page ids which called fit function
1035        :param elapsed: time spent at the fitting level
1036        """
1037        self._update_fit_button(page_id)
1038        msg = "Single Fitting complete "
1039        wx.PostEvent(self.parent, StatusEvent(status=msg, info="info",
1040                                                      type="stop"))
1041        if batch_outputs is None:
1042            batch_outputs = {}
1043        if self.batch_on:
1044            # format batch_outputs
1045            batch_outputs["Chi2"] = []
1046            for index  in range(len(pars)):
1047                batch_outputs[pars[index]] = []
1048                batch_inputs["error on %s" % pars[index]] = []
1049            for res in result:
1050                if res is None:
1051                    null_value = numpy.nan
1052                    from sans.guiframe.data_processor import BatchCell
1053                    cell = BatchCell()
1054                    cell.label = null_value
1055                    cell.value = null_value
1056                    cell.object = [None]
1057                    batch_outputs["Chi2"].append(cell)
1058                    for index  in range(len(pars)):
1059                        batch_outputs[pars[index]].append(null_value)
1060                        item = null_value
1061                        batch_inputs["error on %s" % pars[index]].append(item)
1062                else:
1063                    from sans.guiframe.data_processor import BatchCell
1064                    cell = BatchCell()
1065                    cell.label = res.fitness
1066                    cell.value = res.fitness
1067                    batch_outputs["Chi2"].append(cell)
1068                    for index  in range(len(pars)):
1069                        batch_outputs[pars[index]].append(res.pvec[index])
1070                        item = res.stderr[index]
1071                        batch_inputs["error on %s" % pars[index]].append(item)
1072            pid = page_id[0]
1073            self.page_finder[pid].set_result(batch_inputs=batch_inputs,
1074                                             batch_outputs=batch_outputs)   
1075            count = len(self.page_finder[pid].keys())
1076            for pid in page_id:
1077                cpage = self.fit_panel.get_page_by_id(pid)
1078                cpage._on_fit_complete()
1079                fitproblem_list = self.page_finder[pid].values()
1080                fitproblem_list.sort()
1081                for fitproblem in fitproblem_list:
1082                    data = fitproblem.get_fit_data()
1083                    model = fitproblem.get_model()
1084                    smearer = fitproblem.get_smearer()
1085                    qmin, qmax = fitproblem.get_range()
1086                    weight = fitproblem.get_weight()
1087                    flag = issubclass(data.__class__, Data2D)
1088                    self.draw_model(model=model,
1089                                      page_id=pid, 
1090                                      data=data, 
1091                                      smearer=smearer,
1092                                      enable1D=not flag, 
1093                                      enable2D=flag,
1094                                      state=None,
1095                                      toggle_mode_on=False,
1096                                      fid=data.id,
1097                                      qmin=qmin, qmax=qmax, 
1098                                      update_chisqr=False, 
1099                                      weight=weight)
1100           
1101   
1102       
1103                   
1104    def on_set_batch_result(self, page_id, fid, batch_outputs, batch_inputs):
1105        """
1106        """
1107       
1108        pid =  page_id
1109        if fid not in self.page_finder[pid]:
1110            return
1111        fitproblem = self.page_finder[pid][fid]
1112        index = self.page_finder[pid].nbr_residuals_computed - 1
1113        residuals =  fitproblem.get_residuals()
1114        theory_data = fitproblem.get_theory_data()
1115        data = fitproblem.get_fit_data()
1116        model = fitproblem.get_model()
1117        #fill batch result information
1118        if "Data" not in batch_outputs.keys():
1119            batch_outputs["Data"] = []
1120        from sans.guiframe.data_processor import BatchCell
1121        cell = BatchCell()
1122        cell.label = data.name
1123        cell.value = index
1124        theory_data.id = wx.NewId()
1125        theory_data.name = data.name + "[%s]" % str(model.__class__.__name__)
1126        cell.object = [data, theory_data]
1127       
1128        batch_outputs["Data"].append(cell)
1129        for key, value in data.meta_data.iteritems():
1130            if key not in batch_inputs.keys():
1131                batch_inputs[key] = []
1132            batch_inputs[key].append(value)
1133        param = "temperature"
1134        if hasattr(data.sample, param):
1135            if param not in  batch_inputs.keys():
1136                 batch_inputs[param] = []
1137            batch_inputs[param].append(data.sample.temperature)
1138        # associate residuals plot
1139       
1140        batch_outputs["Chi2"][index].object = [residuals]
1141       
1142    def _single_fit_completed(self, result, pars, page_id, batch_outputs,
1143                          batch_inputs=None,  elapsed=None):
1144        """
1145         Display fit result on one page of the notebook.
1146        :param result: list of object generated when fit ends
1147        :param pars: list of names of parameters fitted
1148        :param page_id: list of page ids which called fit function
1149        :param elapsed: time spent at the fitting level
1150        """ 
1151        self._mac_sleep(0.2)
1152        uid = page_id[0]
1153        if uid in self.fit_thread_list.keys():
1154            del self.fit_thread_list[uid] 
1155         
1156        cpage = self.fit_panel.get_page_by_id(uid)
1157        if cpage.batch_on:
1158            wx.CallAfter(self._batch_single_fit_complete_helper,
1159                          result, pars, page_id, batch_outputs, 
1160                          batch_inputs, elapsed)
1161            return 
1162        else: 
1163            try:
1164                result = result[0]
1165                if result == None:
1166                    self._update_fit_button(page_id)
1167                    msg= "Single Fitting did not converge!!!"
1168                    wx.PostEvent(self.parent, 
1169                                 StatusEvent(status=msg, 
1170                                             info="warning",
1171                                             type="stop"))
1172                    return
1173                if not numpy.isfinite(result.fitness) or \
1174                        numpy.any(result.pvec == None) or \
1175                        not numpy.all(numpy.isfinite(result.pvec)):
1176                    msg = "Single Fitting did not converge!!!"
1177                    wx.PostEvent(self.parent, 
1178                                 StatusEvent(status=msg, 
1179                                             info="warning",
1180                                             type="stop"))
1181                    self._update_fit_button(page_id)
1182                    return
1183               
1184                for uid in page_id:
1185                    cpage = self.fit_panel.get_page_by_id(uid)
1186                    # Make sure we got all results
1187                    #(CallAfter is important to MAC)
1188                    wx.CallAfter(cpage.onsetValues, result.fitness, pars, 
1189                                 result.pvec, result.stderr)
1190                    cpage._on_fit_complete()
1191                if result.stderr == None:
1192                    msg = "Fit Abort: "
1193                else:
1194                    msg = "Fitting: "
1195                msg += "Completed!!!"
1196                wx.PostEvent(self.parent, StatusEvent(status=msg))
1197            except ValueError:
1198                raise
1199                self._update_fit_button(page_id)
1200                msg = "Single Fitting did not converge!!!"
1201                wx.PostEvent(self.parent, StatusEvent(status=msg, info="error",
1202                                                      type="stop"))
1203            except:
1204                raise
1205                self._update_fit_button(page_id)
1206                msg = "Single Fit completed but Following"
1207                msg += " error occurred:%s" % sys.exc_value
1208                wx.PostEvent(self.parent, StatusEvent(status=msg, info="error",
1209                                                      type="stop"))
1210                raise
1211               
1212    def _simul_fit_completed(self, result, page_id, batch_outputs,
1213                             batch_inputs=None,
1214                              pars=None, 
1215                             elapsed=None):
1216        """
1217        Display result of the fit on related panel(s).
1218        :param result: list of object generated when fit ends
1219        :param pars: list of names of parameters fitted
1220        :param page_id: list of page ids which called fit function
1221        :param elapsed: time spent at the fitting level
1222        """
1223        result = result[0]
1224        self.fit_thread_list = {}
1225        if page_id is None:
1226            page_id = []
1227        ## fit more than 1 model at the same time
1228        self._mac_sleep(0.2) 
1229        try:
1230            msg = "" 
1231            if result == None:
1232                self._update_fit_button(page_id)
1233                msg= "Complex Fitting did not converge!!!"
1234                wx.PostEvent(self.parent, StatusEvent(status=msg,
1235                                                      type="stop"))
1236                return
1237            if not numpy.isfinite(result.fitness) or \
1238                numpy.any(result.pvec == None) or not \
1239                numpy.all(numpy.isfinite(result.pvec)):
1240                self._update_fit_button(page_id)
1241                msg= "Simultaneous Fitting did not converge!!!"
1242                wx.PostEvent(self.parent, StatusEvent(status=msg,type="stop"))
1243                return
1244             
1245            for uid in page_id:   
1246                fpdict = self.page_finder[uid]
1247                for value in fpdict.itervalues():
1248                    model = value.get_model()
1249                    data =  value.get_fit_data()
1250                    small_param_name = []
1251                    small_out = []
1252                    small_cov = []
1253                    #Separate result in to data corresponding to each page
1254                    for p in result.parameters:
1255                        model_name, param_name = self.split_string(p.name) 
1256                        if model.name == model_name:
1257                            p_name= model.name+"."+param_name
1258                            if p.name == p_name:     
1259                                if p.value != None and numpy.isfinite(p.value):
1260                                    small_out.append(p.value)
1261                                    small_param_name.append(param_name)
1262                                    small_cov.append(p.stderr)
1263                # Display result on each page
1264                cpage = self.fit_panel.get_page_by_id(uid)
1265                wx.CallAfter(cpage.onsetValues, 
1266                                    result.fitness,
1267                                  small_param_name,
1268                                  small_out,small_cov)
1269                cpage._on_fit_complete()
1270                msg = "Fit completed!"
1271                wx.PostEvent(self.parent, StatusEvent(status=msg))
1272        except Exception:
1273            raise
1274            self._update_fit_button(page_id)
1275            msg = "Complex Fitting did not converge!!!"
1276            wx.PostEvent(self.parent, StatusEvent(status=msg, info="error",
1277                                                  type="stop"))
1278            return
1279        except:
1280            self._update_fit_button(page_id)
1281            msg = "Simultaneous Fit completed"
1282            msg += " but Following error occurred:%s" % sys.exc_value
1283            wx.PostEvent(self.parent, StatusEvent(status=msg, type="stop"))
1284   
1285    def _update_fit_button(self, page_id):
1286        """
1287        Update Fit button when fit stopped
1288       
1289        : parameter page_id: fitpage where the button is
1290        """
1291        if page_id.__class__.__name__ != 'list':
1292            page_id = [page_id]
1293        for uid in page_id: 
1294            page = self.fit_panel.get_page_by_id(uid)
1295            page._on_fit_complete()
1296       
1297    def _on_show_panel(self, event):
1298        """
1299        """
1300        pass
1301       
1302    def _onset_engine_park(self,event):
1303        """
1304        set engine to park
1305        """
1306        self._on_change_engine('park')
1307       
1308    def _onset_engine_scipy(self,event):
1309        """
1310        set engine to scipy
1311        """
1312        self._on_change_engine('scipy')
1313       
1314    def _on_slicer_event(self, event):
1315        """
1316        Receive a panel as event and send it to guiframe
1317       
1318        :param event: event containing a panel
1319       
1320        """
1321        if event.panel is not None:
1322            new_panel = event.panel
1323            self.slicer_panels.append(event.panel)
1324            # Set group ID if available
1325            event_id = self.parent.popup_panel(new_panel)
1326            new_panel.uid = event_id
1327            self.mypanels.append(new_panel) 
1328       
1329    def _onclearslicer(self, event):
1330        """
1331        Clear the boxslicer when close the panel associate with this slicer
1332        """
1333        name =event.GetPane().caption
1334   
1335        for panel in self.slicer_panels:
1336            if panel.window_caption==name:
1337               
1338                for item in self.parent.panels:
1339                    if hasattr(self.parent.panels[item], "uid"):
1340                        if self.parent.panels[item].uid ==panel.base.uid:
1341                            self.parent.panels[item].onClearSlicer(event)
1342                            self.parent._mgr.Update()
1343                            break 
1344                break
1345   
1346    def _return_engine_type(self):
1347        """
1348        return the current type of engine
1349        """
1350        return self._fit_engine
1351     
1352     
1353    def _on_change_engine(self, engine='park'):
1354        """
1355        Allow to select the type of engine to perform fit
1356       
1357        :param engine: the key work of the engine
1358       
1359        """
1360        ## saving fit engine name
1361        self._fit_engine = engine
1362        ## change menu item state
1363        if engine == "park":
1364            self.menu1.FindItemById(self.park_id).Check(True)
1365            self.menu1.FindItemById(self.scipy_id).Check(False)
1366        else:
1367            self.menu1.FindItemById(self.park_id).Check(False)
1368            self.menu1.FindItemById(self.scipy_id).Check(True)
1369        ## post a message to status bar
1370        msg = "Engine set to: %s" % self._fit_engine
1371        wx.PostEvent(self.parent, 
1372                     StatusEvent(status=msg))
1373        ## send the current engine type to fitpanel
1374        self.fit_panel._on_engine_change(name=self._fit_engine)
1375
1376       
1377    def _on_model_panel(self, evt):
1378        """
1379        react to model selection on any combo box or model menu.plot the model 
1380       
1381        :param evt: wx.combobox event
1382       
1383        """
1384        model = evt.model
1385        uid = evt.uid
1386        qmin = evt.qmin
1387        qmax = evt.qmax
1388        smearer = evt.smearer
1389        caption = evt.caption
1390        enable_smearer = evt.enable_smearer
1391        if model == None:
1392            return
1393        if uid not in self.page_finder.keys():
1394            return
1395        # save the name containing the data name with the appropriate model
1396        self.page_finder[uid].set_model(model)
1397        self.page_finder[uid].enable_smearing(enable_smearer)
1398        self.page_finder[uid].set_range(qmin=qmin, qmax=qmax)
1399        self.page_finder[uid].set_fit_tab_caption(caption=caption)
1400        if self.sim_page is not None and not self.batch_on:
1401            self.sim_page.draw_page()
1402       
1403    def _update1D(self, x, output):
1404        """
1405        Update the output of plotting model 1D
1406        """
1407        msg = "Plot updating ... "
1408        wx.PostEvent(self.parent, StatusEvent(status=msg,type="update"))
1409       
1410    def _complete1D(self, x, y, page_id, elapsed, index, model,
1411                    weight=None, fid=None,
1412                    toggle_mode_on=False, state=None, 
1413                    data=None, update_chisqr=True):
1414        """
1415        Complete plotting 1D data
1416        """ 
1417        try:
1418            new_plot = Data1D(x=x, y=y)
1419            new_plot.is_data = False
1420            new_plot.dy = numpy.zeros(len(y))
1421            new_plot.symbol = GUIFRAME_ID.CURVE_SYMBOL_NUM
1422            _yaxis, _yunit = data.get_yaxis() 
1423            _xaxis, _xunit = data.get_xaxis() 
1424            new_plot.title = data.name
1425
1426            new_plot.group_id = self.page_finder[page_id].get_graph_id()
1427            if new_plot.group_id == None:
1428                new_plot.group_id = data.group_id
1429            new_plot.id =  str(page_id) + "model"
1430            #if new_plot.id in self.color_dict:
1431            #    new_plot.custom_color = self.color_dict[new_plot.id]
1432            #find if this theory was already plotted and replace that plot given
1433            #the same id
1434            theory_data = self.page_finder[page_id].get_theory_data(fid=data.id)
1435            new_plot.name = model.name + " ["+ str(model.__class__.__name__)+"]"
1436            new_plot.xaxis(_xaxis, _xunit)
1437            new_plot.yaxis(_yaxis, _yunit)
1438            self.page_finder[page_id].set_theory_data(data=new_plot, 
1439                                                      fid=data.id)
1440            self.parent.update_theory(data_id=data.id, theory=new_plot,
1441                                       state=state)   
1442            current_pg = self.fit_panel.get_page_by_id(page_id)
1443            title = new_plot.title
1444            wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot,
1445                                            title=str(title)))
1446            caption = current_pg.window_caption
1447            self.page_finder[page_id].set_fit_tab_caption(caption=caption)
1448            self.page_finder[page_id].set_theory_data(data=new_plot, 
1449                                                      fid=data.id)
1450            if toggle_mode_on:
1451                wx.PostEvent(self.parent, 
1452                             NewPlotEvent(group_id=str(page_id) + " Model2D",
1453                                               action="Hide"))
1454            else:
1455                if update_chisqr:
1456                    wx.PostEvent(current_pg,
1457                                 Chi2UpdateEvent(output=self._cal_chisqr(
1458                                                                data=data,
1459                                                                fid=fid,
1460                                                                weight=weight,
1461                                                            page_id=page_id,
1462                                                            index=index)))
1463                else:
1464                    self._plot_residuals(page_id=page_id, data=data, fid=fid,
1465                                          index=index, weight=weight)
1466
1467            msg = "Computation  completed!"
1468            wx.PostEvent( self.parent, StatusEvent(status=msg, type="stop" ))
1469        except:
1470            raise
1471            #msg = " Error occurred when drawing %s Model 1D: " % new_plot.name
1472            #msg += " %s"  % sys.exc_value
1473            #wx.PostEvent(self.parent, StatusEvent(status=msg, type="stop"))
1474   
1475    def _update2D(self, output,time=None):
1476        """
1477        Update the output of plotting model
1478        """
1479        wx.PostEvent(self.parent, StatusEvent(status="Plot \
1480        #updating ... ", type="update"))
1481        #self.ready_fit()
1482 
1483    def _complete2D(self, image, data, model, page_id,  elapsed, index, qmin,
1484                qmax, fid=None, weight=None, toggle_mode_on=False, state=None, 
1485                     update_chisqr=True):
1486        """
1487        Complete get the result of modelthread and create model 2D
1488        that can be plot.
1489        """
1490        new_plot= Data2D(image=image, err_image=data.err_data)
1491        new_plot.name = model.name
1492        new_plot.title = "Analytical model 2D "
1493        new_plot.id = str(page_id) + "model"
1494        new_plot.group_id = str(page_id) + " Model2D"
1495        new_plot.detector = data.detector
1496        new_plot.source = data.source
1497        new_plot.is_data = False 
1498        new_plot.qx_data = data.qx_data
1499        new_plot.qy_data = data.qy_data
1500        new_plot.q_data = data.q_data
1501        new_plot.mask = data.mask
1502        ## plot boundaries
1503        new_plot.ymin = data.ymin
1504        new_plot.ymax = data.ymax
1505        new_plot.xmin = data.xmin
1506        new_plot.xmax = data.xmax
1507        title = data.title
1508        if len(title) > 1:
1509            new_plot.title = "Model2D for " + data.name
1510        new_plot.is_data = False
1511        new_plot.name = model.name + " [" + \
1512                                    str(model.__class__.__name__) + "-2D]"
1513        theory_data = deepcopy(new_plot)
1514        theory_data.name = "Unknown"
1515       
1516        self.page_finder[page_id].set_theory_data(data=theory_data, fid=data.id)
1517        self.parent.update_theory(data_id=data.id, 
1518                                       theory=new_plot,
1519                                       state=state) 
1520        current_pg = self.fit_panel.get_page_by_id(page_id)
1521        title = new_plot.title
1522        wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot,
1523                                               title=title))
1524        self.page_finder[page_id].set_theory_data(data=new_plot, fid=data.id)
1525        if toggle_mode_on:
1526            wx.PostEvent(self.parent, 
1527                             NewPlotEvent(group_id=str(page_id) + " Model1D",
1528                                               action="Hide"))
1529        else:
1530            # Chisqr in fitpage
1531            if update_chisqr:
1532                wx.PostEvent(current_pg,
1533                             Chi2UpdateEvent(output=self._cal_chisqr(data=data,
1534                                                                    weight=weight,
1535                                                                    fid=fid,
1536                                                         page_id=page_id,
1537                                                         index=index)))
1538            else:
1539                self._plot_residuals(page_id=page_id, data=data, fid=fid,
1540                                      index=index, weight=weight)
1541        msg = "Computation  completed!"
1542        wx.PostEvent(self.parent, StatusEvent(status=msg, type="stop"))
1543   
1544    def _draw_model2D(self, model, page_id, qmin,
1545                      qmax,
1546                      data=None, smearer=None,
1547                      description=None, enable2D=False,
1548                      state=None,
1549                      fid=None,
1550                      weight=None,
1551                      toggle_mode_on=False,
1552                       update_chisqr=True):
1553        """
1554        draw model in 2D
1555       
1556        :param model: instance of the model to draw
1557        :param description: the description of the model
1558        :param enable2D: when True allows to draw model 2D
1559        :param qmin: the minimum value to  draw model 2D
1560        :param qmax: the maximum value to draw model 2D
1561        :param qstep: the number of division of Qx and Qy of the model to draw
1562           
1563        """
1564        if not enable2D:
1565            return None
1566        try:
1567            from model_thread import Calc2D
1568            ## If a thread is already started, stop it
1569            if (self.calc_2D is not None) and self.calc_2D.isrunning():
1570                self.calc_2D.stop()
1571            self.calc_2D = Calc2D(model=model, 
1572                                    data=data,
1573                                    page_id=page_id,
1574                                    smearer=smearer,
1575                                    qmin=qmin,
1576                                    qmax=qmax,
1577                                    weight=weight, 
1578                                    fid=fid,
1579                                    toggle_mode_on=toggle_mode_on,
1580                                    state=state,
1581                                    completefn=self._complete2D,
1582                                    update_chisqr=update_chisqr)
1583            self.calc_2D.queue()
1584
1585        except:
1586            raise
1587            #msg = " Error occurred when drawing %s Model 2D: " % model.name
1588            #msg += " %s" % sys.exc_value
1589            #wx.PostEvent(self.parent, StatusEvent(status=msg))
1590
1591    def _draw_model1D(self, model, page_id, data, 
1592                      qmin, qmax, smearer=None,
1593                state=None,
1594                weight=None,
1595                fid=None, 
1596                toggle_mode_on=False, update_chisqr=True, 
1597                enable1D=True):
1598        """
1599        Draw model 1D from loaded data1D
1600       
1601        :param data: loaded data
1602        :param model: the model to plot
1603       
1604        """
1605        if not enable1D:
1606            return 
1607        try:
1608            from model_thread import Calc1D
1609            ## If a thread is already started, stop it
1610            if (self.calc_1D is not None) and self.calc_1D.isrunning():
1611                self.calc_1D.stop()
1612            self.calc_1D = Calc1D(data=data,
1613                                  model=model,
1614                                  page_id=page_id, 
1615                                  qmin=qmin,
1616                                  qmax=qmax,
1617                                  smearer=smearer,
1618                                  state=state,
1619                                  weight=weight,
1620                                  fid=fid,
1621                                  toggle_mode_on=toggle_mode_on,
1622                                  completefn=self._complete1D,
1623                                  #updatefn = self._update1D,
1624                                  update_chisqr=update_chisqr)
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            theory_data = self.page_finder[page_id].get_theory_data(fid=data_copy.id)
1676            if theory_data== None:
1677                return chisqr
1678            gn = theory_data.y
1679            en = dy[index]
1680           
1681        # residual
1682        res = (fn - gn) / en
1683        residuals = res[numpy.isfinite(res)]
1684        # get chisqr only w/finite
1685        chisqr = numpy.average(residuals * residuals)
1686        self._plot_residuals(page_id=page_id, data=data_copy, 
1687                             fid=fid,
1688                             weight=weight, index=index)
1689       
1690        return chisqr
1691   
1692    def _plot_residuals(self, page_id, weight, fid=None,
1693                        data=None, index=None): 
1694        """
1695        Plot the residuals
1696       
1697        :param data: data
1698        :param index: index array (bool)
1699        : Note: this is different from the residuals in cal_chisqr()
1700        """
1701        data_copy = deepcopy(data)
1702        # Get data: data I, theory I, and data dI in order
1703        if data_copy.__class__.__name__ == "Data2D":
1704            # build residuals
1705            residuals = Data2D()
1706            #residuals.copy_from_datainfo(data)
1707            # Not for trunk the line below, instead use the line above
1708            data_copy.clone_without_data(len(data_copy.data), residuals)
1709            residuals.data = None
1710            fn = data_copy.data#[index]
1711            theory_data = self.page_finder[page_id].get_theory_data(fid=data_copy.id)
1712            gn = theory_data.data#[index]
1713            if weight == None:
1714                en = data_copy.err_data
1715            else:
1716                en = weight
1717            residuals.data = (fn - gn) / en
1718            residuals.qx_data = data_copy.qx_data#[index]
1719            residuals.qy_data = data_copy.qy_data #[index]
1720            residuals.q_data = data_copy.q_data#[index]
1721            residuals.err_data = numpy.ones(len(residuals.data))#[index]
1722            residuals.xmin = min(residuals.qx_data)
1723            residuals.xmax = max(residuals.qx_data)
1724            residuals.ymin = min(residuals.qy_data)
1725            residuals.ymax = max(residuals.qy_data)
1726            residuals.q_data = data_copy.q_data#[index]
1727            residuals.mask = data_copy.mask
1728            residuals.scale = 'linear'
1729            # check the lengths
1730            if len(residuals.data) != len(residuals.q_data):
1731                return
1732        else:
1733            # 1 d theory from model_thread is only in the range of index
1734            if data_copy.dy == None or data_copy.dy == []:
1735                dy = numpy.ones(len(data_copy.y))
1736            else:
1737                if weight == None:
1738                    dy = numpy.ones(len(data_copy.y))
1739                ## Set consitently w/AbstractFitengine:
1740                ## But this should be corrected later.
1741                else:
1742                    dy = weight#deepcopy(data_copy.dy)
1743                dy[dy==0] = 1 
1744            fn = data_copy.y[index] 
1745            theory_data = self.page_finder[page_id].get_theory_data(fid=data_copy.id)
1746            gn = theory_data.y
1747            en = dy[index]
1748            # build residuals
1749            residuals = Data1D()
1750            residuals.y = (fn - gn) / en
1751            residuals.x = data_copy.x[index]
1752            residuals.dy = numpy.ones(len(residuals.y))
1753            residuals.dx = None
1754            residuals.dxl = None
1755            residuals.dxw = None
1756            residuals.ytransform = 'y'
1757            # For latter scale changes
1758            residuals.xaxis('\\rm{Q} ', 'A^{-1}')
1759            residuals.yaxis('\\rm{Residuals} ', 'normalized')
1760        new_plot = residuals
1761        new_plot.name = "Residuals for " + str(data.name)
1762        ## allow to highlight data when plotted
1763        new_plot.interactive = True
1764        ## when 2 data have the same id override the 1 st plotted
1765        new_plot.id = "res" + str(data_copy.id)#name + " residuals"
1766        ##group_id specify on which panel to plot this data
1767        group_id = self.page_finder[page_id].get_graph_id()
1768        if group_id == None:
1769            group_id = data.group_id
1770        new_plot.group_id ="res" + str(group_id)
1771        #new_plot.is_data = True
1772        ##post data to plot
1773        title = new_plot.name
1774        self.page_finder[page_id].set_residuals(residuals=new_plot, fid=data.id)
1775        batch_on = self.fit_panel.get_page_by_id(page_id).batch_on
1776        if not batch_on:
1777            wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot, title=title))
1778            return
1779        #reset weight 
1780        #self.weight = None
1781        # Need all residuals before plotting
1782        # Should be refactored
1783        n = len(self.page_finder[page_id].keys())
1784        m = self.page_finder[page_id].nbr_residuals_computed
1785        flag = False
1786        batch_inputs, batch_outputs = self.page_finder[page_id].get_result()
1787         
1788        if self.page_finder[page_id].nbr_residuals_computed == -1:
1789            flag = False
1790        else:
1791            if m == n -1:
1792                flag = True
1793            else:
1794                flag = False
1795            self.page_finder[page_id].nbr_residuals_computed += 1
1796           
1797            self.on_set_batch_result(page_id=page_id,
1798                                 fid=fid,
1799                              batch_outputs=batch_outputs, 
1800                                batch_inputs=batch_inputs) 
1801        # plot data
1802        event = BatchDrawEvent(page_id=page_id , 
1803                           batch_on=batch_on,
1804                           batch_outputs=batch_outputs,
1805                           batch_inputs=batch_inputs,
1806                           is_displayed=flag)
1807        wx.PostEvent(self.parent, event)
1808       
1809
1810       
1811    def on_display_grid(self, event):
1812        """
1813        deplay batch result
1814        """
1815        page_id = event.page_id
1816        batch_on = event.batch_on
1817        flag = event.is_displayed
1818        batch_outputs = event.batch_outputs
1819        batch_inputs = event.batch_inputs
1820        if flag and batch_on:
1821            self.parent.on_set_batch_result(data_outputs=batch_outputs, 
1822                                            data_inputs=batch_inputs,
1823                                            plugin_name=self.sub_menu)
1824            self.page_finder[page_id].nbr_residuals_computed = -1
1825       
1826#def profile(fn, *args, **kw):
1827#    import cProfile, pstats, os
1828#    global call_result
1829#    def call():
1830#        global call_result
1831#        call_result = fn(*args, **kw)
1832#    cProfile.runctx('call()', dict(call=call), {}, 'profile.out')
1833#    stats = pstats.Stats('profile.out')
1834#    #stats.sort_stats('time')
1835#    stats.sort_stats('calls')
1836#    stats.print_stats()
1837#    os.unlink('profile.out')
1838#    return call_result
1839if __name__ == "__main__":
1840    i = Plugin()
1841   
1842   
1843   
1844   
Note: See TracBrowser for help on using the repository browser.