source: sasview/fittingview/src/sans/perspectives/fitting/fitting.py @ 90ddcdb

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

2d fix for batch plot

  • Property mode set to 100644
File size: 76.7 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        flag = True
700        ##  count the number of fitproblem schedule to fit
701        fitproblem_count = 0
702        for value in self.page_finder.values():
703            if value.get_scheduled() == 1:
704                fitproblem_count += 1
705               
706        self.fitproblem_count = fitproblem_count 
707        if self._fit_engine == "park":
708            engineType = "Simultaneous Fit"
709        else:
710            engineType = "Single Fit"
711        fitter_list = []       
712        sim_fitter = None     
713        is_single_fit = True
714        if self.sim_page is not None and self.sim_page.uid == uid:
715            #simulatanous fit only one engine need to be created
716            ## if simultaneous fit change automatically the engine to park
717            self._on_change_engine(engine='park')   
718            sim_fitter = Fit(self._fit_engine) 
719            fitter_list.append(sim_fitter) 
720            is_single_fit = False
721
722        self.fitproblem_count = fitproblem_count 
723        if self._fit_engine == "park":
724            engineType = "Simultaneous Fit"
725        else:
726            engineType = "Single Fit"
727       
728        self.current_pg = None
729        list_page_id = []
730        fit_id = 0
731        batch_inputs = {}
732        batch_outputs = {}
733        for page_id, value in self.page_finder.iteritems():
734            # For simulfit (uid give with None), do for-loop
735            # if uid is specified (singlefit), do it only on the page.
736            if engineType == "Single Fit":
737                if page_id != uid:
738                    continue
739            try:
740                if value.get_scheduled() == 1:
741                    value.nbr_residuals_computed = 0
742                    #Get list of parameters name to fit
743                    pars = []
744                    templist = []
745                    page = self.fit_panel.get_page_by_id(page_id)
746                    self.set_fit_weight(uid=page.uid, 
747                                     flag=page.get_weight_flag(),
748                                     is2d = page._is_2D())
749                    templist = page.get_param_list()
750                    flag = page._update_paramv_on_fit() 
751                    if not flag:
752                        msg = "Fitting range or parameter values are"
753                        msg += " invalid in %s"% \
754                                    page.window_caption
755                        wx.PostEvent(page.parent.parent, 
756                                     StatusEvent(status= msg, info="error",
757                                     type="stop"))
758                        return flag
759                    for element in templist:
760                        name = str(element[1])
761                        pars.append(name)
762                    fitproblem_list = value.values()
763                    for fitproblem in  fitproblem_list:
764                        if sim_fitter is None:
765                            fitter = Fit(self._fit_engine) 
766                            self._fit_helper(fitproblem=fitproblem, 
767                                                pars=pars, 
768                                                fitter=fitter,
769                                              fit_id=fit_id, 
770                                              batch_inputs=batch_inputs,
771                                              batch_outputs=batch_outputs)
772                            fitter_list.append(fitter) 
773                        else:
774                            fitter = sim_fitter
775                            self._fit_helper(fitproblem=fitproblem, 
776                                                pars=pars, 
777                                                fitter=fitter,
778                                              fit_id=fit_id, 
779                                              batch_inputs=batch_inputs,
780                                              batch_outputs=batch_outputs)
781                        fit_id += 1
782                    list_page_id.append(page_id)
783                    current_page_id = page_id
784                    value.clear_model_param()
785            except:
786                flag = False
787                msg= "%s error: %s" % (engineType, sys.exc_value)
788                wx.PostEvent(self.parent, StatusEvent(status=msg, info="error",
789                                                      type="stop"))
790                return flag
791        ## If a thread is already started, stop it
792        #if self.calc_fit!= None and self.calc_fit.isrunning():
793        #    self.calc_fit.stop()
794        msg = "Fitting is in progress..."
795        wx.PostEvent( self.parent, StatusEvent(status=msg, type="progress" ))
796       
797        #Handler used for park engine displayed message
798        handler = ConsoleUpdate(parent=self.parent,
799                                manager=self,
800                                improvement_delta=0.1)
801        self._mac_sleep(0.2)
802        ## perform single fit
803        if is_single_fit:
804            calc_fit = FitThread(handler = handler,
805                                    fn=fitter_list,
806                                    pars=pars,
807                                    batch_inputs=batch_inputs,
808                                    batch_outputs=batch_outputs,
809                                    page_id=list_page_id,
810                                    completefn=self._single_fit_completed,
811                                    ftol=self.ftol)
812        else:
813            current_page_id = self.sim_page.uid
814            ## Perform more than 1 fit at the time
815            calc_fit = FitThread(handler=handler,
816                                    fn=fitter_list,
817                                    batch_inputs=batch_inputs,
818                                    batch_outputs=batch_outputs,
819                                    page_id=list_page_id,
820                                    updatefn=handler.update_fit,
821                                    completefn=self._simul_fit_completed,
822                                    ftol=self.ftol)
823        self.fit_thread_list[current_page_id] = calc_fit
824        calc_fit.queue()
825        msg = "Fitting is in progress..."
826        wx.PostEvent( self.parent, StatusEvent(status=msg, type="progress" ))
827       
828        self.ready_fit(calc_fit=calc_fit)
829        return flag
830   
831    def ready_fit(self, calc_fit):
832        """
833        Ready for another fit
834        """
835        if self.fitproblem_count != None and self.fitproblem_count > 1:
836            calc_fit.ready(2.5)
837        else:
838            time.sleep(0.4)
839           
840    def remove_plot(self, uid, fid=None, theory=False):
841        """
842        remove model plot when a fit page is closed
843        :param uid: the id related to the fitpage to close
844        :param fid: the id of the fitproblem(data, model, range,etc)
845        """
846        if uid not in self.page_finder.keys():
847            return
848        fitproblemList = self.page_finder[uid].get_fit_problem(fid)
849        for fitproblem in fitproblemList:
850            data = fitproblem.get_fit_data()
851            model = fitproblem.get_model()
852            plot_id = None
853            if model is not None:
854                plot_id = data.id + name
855            if theory:
856                plot_id = data.id
857            group_id = data.group_id
858            wx.PostEvent(self.parent, NewPlotEvent(id=plot_id,
859                                                       group_id=group_id,
860                                                       action='remove'))
861           
862    def store_data(self, uid, data_list=None, caption=None):
863        """
864        Recieve a list of data and store them ans well as a caption of
865        the fit page where they come from.
866        :param uid: if related to a fit page
867        :param data_list: list of data to fit
868        :param caption: caption of the window related to these data
869        """
870        if data_list is None:
871            data_list = []
872       
873        self.page_finder[uid].set_fit_data(data=data_list)
874        if caption is not None:
875            self.page_finder[uid].set_fit_tab_caption(caption=caption)
876           
877    def on_add_new_page(self, event=None):
878        """
879        ask fit panel to create a new empty page
880        """
881        try:
882            page = self.fit_panel.add_empty_page()
883            page_caption = page.window_caption
884            # add data associated to the page created
885            if page != None: 
886                wx.PostEvent(self.parent, StatusEvent(status="Page Created",
887                                               info="info"))
888            else:
889                msg = "Page was already Created"
890                wx.PostEvent(self.parent, StatusEvent(status=msg,
891                                                       info="warning"))
892            self.set_top_panel()
893        except:
894            msg = "Creating Fit page: %s"%sys.exc_value
895            wx.PostEvent(self.parent, StatusEvent(status=msg, info="error"))
896       
897    def add_fit_page(self, data):
898        """
899        given a data, ask to the fitting panel to create a new fitting page,
900        get this page and store it into the page_finder of this plug-in
901        :param data: is a list of data
902        """
903        page = self.fit_panel.set_data(data)
904        # page could be None when loading state files
905        if page == None:
906            return page
907        page_caption = page.window_caption
908        #append Data1D to the panel containing its theory
909        #if theory already plotted
910        if page.uid in self.page_finder:
911            data = page.get_data()
912            theory_data = self.page_finder[page.uid].get_theory_data(data.id)
913            if issubclass(data.__class__, Data2D):
914                data.group_id = wx.NewId()
915                if theory_data is not None:
916                    group_id = str(page.uid) + " Model1D"
917                    wx.PostEvent(self.parent, 
918                             NewPlotEvent(group_id=group_id,
919                                               action="delete"))
920                    self.parent.update_data(prev_data=theory_data,
921                                             new_data=data)     
922            else:
923                if theory_data is not None:
924                    group_id = str(page.uid) + " Model2D"
925                    data.group_id = theory_data.group_id
926                    wx.PostEvent(self.parent, 
927                             NewPlotEvent(group_id=group_id,
928                                               action="delete"))
929                    self.parent.update_data(prev_data=theory_data,
930                                             new_data=data)   
931        self.store_data(uid=page.uid, data_list=page.get_data_list(), 
932                        caption=page.window_caption)
933        if self.sim_page is not None and not self.batch_on:
934            self.sim_page.draw_page()
935        return page
936           
937    def _onEVT_SLICER_PANEL(self, event):
938        """
939        receive and event telling to update a panel with a name starting with
940        event.panel_name. this method update slicer panel
941        for a given interactor.
942       
943        :param event: contains type of slicer , paramaters for updating
944            the panel and panel_name to find the slicer 's panel concerned.
945        """
946        for item in self.parent.panels:
947            name = event.panel_name
948            if self.parent.panels[item].window_caption.startswith(name):
949                self.parent.panels[item].set_slicer(event.type, event.params)
950               
951        self.parent._mgr.Update()
952   
953    def _closed_fitpage(self, event):   
954        """
955        request fitpanel to close a given page when its unique data is removed
956        from the plot. close fitpage only when the a loaded data is removed
957        """   
958        if event is None or event.data is None:
959            return
960        if hasattr(event.data,"is_data"):
961            if not event.data.is_data or \
962                event.data.__class__.__name__ == "Data1D":
963                self.fit_panel.close_page_with_data(event.data) 
964 
965    def _reset_schedule_problem(self, value=0, uid=None):
966        """
967        unschedule or schedule all fitproblem to be fit
968        """
969        # case that uid is not specified
970        if uid == None:
971            for page_id in self.page_finder.keys():
972                self.page_finder[page_id].schedule_tofit(value)
973        # when uid is given
974        else:
975            if uid in self.page_finder.keys():
976                self.page_finder[uid].schedule_tofit(value)
977               
978    def _fit_helper(self, fitproblem, pars, fitter, fit_id,
979                    batch_inputs, batch_outputs):
980        """
981        Create and set fit engine with series of data and model
982        :param pars: list of fittable parameters
983        :param fitter_list: list of fit engine
984        :param value:  structure storing data mapped to their model, range etc..
985        """
986        data = fitproblem.get_fit_data()
987        model = fitproblem.get_model()
988        smearer = fitproblem.get_smearer()
989        qmin, qmax = fitproblem.get_range()
990
991        #Extra list of parameters and their constraints
992        listOfConstraint = []
993        param = fitproblem.get_model_param()
994        if len(param) > 0:
995            for item in param:
996                ## check if constraint
997                if item[0] != None and item[1] != None:
998                    listOfConstraint.append((item[0],item[1]))
999        new_model = model#deepcopy(model)
1000        fitter.set_model(new_model, fit_id, pars, data=data,
1001                         constraints=listOfConstraint)
1002        fitter.set_data(data=data, id=fit_id, smearer=smearer, qmin=qmin, 
1003                        qmax=qmax)
1004        fitter.select_problem_for_fit(id=fit_id, value=1)
1005       
1006   
1007    def _onSelect(self,event):
1008        """
1009        when Select data to fit a new page is created .Its reference is
1010        added to self.page_finder
1011        """
1012        self.panel = event.GetEventObject()
1013        Plugin.on_perspective(self, event=event)
1014        self.select_data(self.panel)
1015       
1016    def select_data(self, panel):
1017        """
1018        """
1019        self.panel = panel
1020        for plottable in self.panel.graph.plottables:
1021            if plottable.__class__.__name__ in ["Data1D", "Theory1D"]:
1022                data_id = self.panel.graph.selected_plottable
1023                if plottable == self.panel.plots[data_id]:
1024                    data = plottable
1025                    self.add_fit_page(data=[data])
1026                    return
1027            else:
1028                data = plottable
1029                self.add_fit_page(data=[data])
1030        self.set_top_panel()
1031           
1032    def update_fit(self, result=None, msg=""):
1033        """
1034        """
1035        print "update_fit result", result
1036       
1037   
1038       
1039    def _batch_single_fit_complete_helper(self, result, pars, page_id, 
1040                            batch_outputs, batch_inputs, elapsed=None):
1041        """
1042        Display fit result in batch
1043        :param result: list of objects received fromt fit engines
1044        :param pars: list of  fitted parameters names
1045        :param page_id: list of page ids which called fit function
1046        :param elapsed: time spent at the fitting level
1047        """
1048        self._update_fit_button(page_id)
1049        msg = "Single Fitting complete "
1050        wx.PostEvent(self.parent, StatusEvent(status=msg, info="info",
1051                                                      type="stop"))
1052        pid = page_id[0]
1053        if batch_outputs is None:
1054            batch_outputs = {}
1055        if self.batch_on:
1056            # format batch_outputs
1057            batch_outputs["Chi2"] = []
1058            for index  in range(len(pars)):
1059                batch_outputs[pars[index]] = []
1060                batch_inputs["error on %s" % pars[index]] = []
1061            for res in result:
1062                model, data = res.inputs[0]
1063                temp_model = model#.clone()
1064                if res is None:
1065                    null_value = numpy.nan
1066                    from sans.guiframe.data_processor import BatchCell
1067                    cell = BatchCell()
1068                    cell.label = null_value
1069                    cell.value = null_value
1070                    cell.object = [None]
1071                    batch_outputs["Chi2"].append(cell)
1072                    for index  in range(len(pars)):
1073                        batch_outputs[pars[index]].append(null_value)
1074                        item = null_value
1075                        batch_inputs["error on %s" % pars[index]].append(item)
1076                        if pars[index] in model.getParamList():
1077                            temp_model.setParam(pars[index], res.pvec[index])
1078                else:
1079                    from sans.guiframe.data_processor import BatchCell
1080                    cell = BatchCell()
1081                    cell.label = res.fitness
1082                    cell.value = res.fitness
1083                    batch_outputs["Chi2"].append(cell)
1084                    for index  in range(len(pars)):
1085                        batch_outputs[pars[index]].append(res.pvec[index])
1086                        item = res.stderr[index]
1087                        batch_inputs["error on %s" % pars[index]].append(item)
1088                        if pars[index] in temp_model.getParamList():
1089                            temp_model.setParam(pars[index], res.pvec[index])
1090                       
1091                self.page_finder[pid].set_batch_result(batch_inputs=batch_inputs,
1092                                                 batch_outputs=batch_outputs)   
1093                cpage = self.fit_panel.get_page_by_id(pid)
1094                cpage._on_fit_complete()
1095                self.page_finder[pid][data.id].set_result(res)
1096                fitproblem = self.page_finder[pid][data.id]
1097               
1098               
1099                from sans.models.qsmearing import smear_selection
1100                #smearer = fitproblem.get_smearer()
1101                smearer = smear_selection(data, temp_model)
1102                qmin, qmax = fitproblem.get_range()
1103                weight = fitproblem.get_weight()
1104                flag = issubclass(data.__class__, Data2D)
1105                self.draw_model(model=temp_model,
1106                                  page_id=pid, 
1107                                  data=data, 
1108                                  smearer=smearer,
1109                                  enable1D=not flag, 
1110                                  enable2D=flag,
1111                                  state=None,
1112                                  toggle_mode_on=False,
1113                                  fid=data.id,
1114                                  qmin=qmin, qmax=qmax, 
1115                                  update_chisqr=False, 
1116                                  weight=weight)
1117       
1118    def on_set_batch_result(self, page_id, fid, batch_outputs, batch_inputs):
1119        """
1120        """
1121       
1122        pid =  page_id
1123        if fid not in self.page_finder[pid]:
1124            return
1125        fitproblem = self.page_finder[pid][fid]
1126        index = self.page_finder[pid].nbr_residuals_computed - 1
1127        residuals =  fitproblem.get_residuals()
1128        theory_data = fitproblem.get_theory_data()
1129        data = fitproblem.get_fit_data()
1130        model = fitproblem.get_model()
1131        #fill batch result information
1132        if "Data" not in batch_outputs.keys():
1133            batch_outputs["Data"] = []
1134        from sans.guiframe.data_processor import BatchCell
1135        cell = BatchCell()
1136        cell.label = data.name
1137        cell.value = index
1138        theory_data.id = wx.NewId()
1139        theory_data.name = data.name + "[%s]" % str(model.__class__.__name__)
1140        cell.object = [data, theory_data]
1141        batch_outputs["Data"].append(cell)
1142        for key, value in data.meta_data.iteritems():
1143            if key not in batch_inputs.keys():
1144                batch_inputs[key] = []
1145            batch_inputs[key].append(value)
1146        param = "temperature"
1147        if hasattr(data.sample, param):
1148            if param not in  batch_inputs.keys():
1149                 batch_inputs[param] = []
1150            batch_inputs[param].append(data.sample.temperature)
1151        # associate residuals plot
1152        batch_outputs["Chi2"][index].object = [residuals]
1153       
1154    def _single_fit_completed(self, result, pars, page_id, batch_outputs,
1155                          batch_inputs=None,  elapsed=None):
1156        """
1157         Display fit result on one page of the notebook.
1158        :param result: list of object generated when fit ends
1159        :param pars: list of names of parameters fitted
1160        :param page_id: list of page ids which called fit function
1161        :param elapsed: time spent at the fitting level
1162        """ 
1163        self._mac_sleep(0.2)
1164        uid = page_id[0]
1165        if uid in self.fit_thread_list.keys():
1166            del self.fit_thread_list[uid] 
1167         
1168        cpage = self.fit_panel.get_page_by_id(uid)
1169        if cpage.batch_on:
1170            wx.CallAfter(self._batch_single_fit_complete_helper,
1171                          result, pars, page_id, batch_outputs, 
1172                          batch_inputs, elapsed)
1173            return 
1174        else: 
1175            try:
1176                result = result[0]
1177                if result == None:
1178                    self._update_fit_button(page_id)
1179                    msg= "Single Fitting did not converge!!!"
1180                    wx.PostEvent(self.parent, 
1181                                 StatusEvent(status=msg, 
1182                                             info="warning",
1183                                             type="stop"))
1184                    return
1185                if not numpy.isfinite(result.fitness) or \
1186                        numpy.any(result.pvec == None) or \
1187                        not numpy.all(numpy.isfinite(result.pvec)):
1188                    msg = "Single Fitting did not converge!!!"
1189                    wx.PostEvent(self.parent, 
1190                                 StatusEvent(status=msg, 
1191                                             info="warning",
1192                                             type="stop"))
1193                    self._update_fit_button(page_id)
1194                    return
1195               
1196                for uid in page_id:
1197                    cpage = self.fit_panel.get_page_by_id(uid)
1198                    # Make sure we got all results
1199                    #(CallAfter is important to MAC)
1200                    wx.CallAfter(cpage.onsetValues, result.fitness, pars, 
1201                                 result.pvec, result.stderr)
1202                    cpage._on_fit_complete()
1203                if result.stderr == None:
1204                    msg = "Fit Abort: "
1205                else:
1206                    msg = "Fitting: "
1207                msg += "Completed!!!"
1208                wx.PostEvent(self.parent, StatusEvent(status=msg))
1209            except ValueError:
1210                raise
1211                self._update_fit_button(page_id)
1212                msg = "Single Fitting did not converge!!!"
1213                wx.PostEvent(self.parent, StatusEvent(status=msg, info="error",
1214                                                      type="stop"))
1215            except:
1216                raise
1217                self._update_fit_button(page_id)
1218                msg = "Single Fit completed but Following"
1219                msg += " error occurred:%s" % sys.exc_value
1220                wx.PostEvent(self.parent, StatusEvent(status=msg, info="error",
1221                                                      type="stop"))
1222                raise
1223               
1224    def _simul_fit_completed(self, result, page_id, batch_outputs,
1225                             batch_inputs=None,
1226                              pars=None, 
1227                             elapsed=None):
1228        """
1229        Display result of the fit on related panel(s).
1230        :param result: list of object generated when fit ends
1231        :param pars: list of names of parameters fitted
1232        :param page_id: list of page ids which called fit function
1233        :param elapsed: time spent at the fitting level
1234        """
1235        result = result[0]
1236        self.fit_thread_list = {}
1237        if page_id is None:
1238            page_id = []
1239        ## fit more than 1 model at the same time
1240        self._mac_sleep(0.2) 
1241        try:
1242            msg = "" 
1243            if result == None:
1244                self._update_fit_button(page_id)
1245                msg= "Complex Fitting did not converge!!!"
1246                wx.PostEvent(self.parent, StatusEvent(status=msg,
1247                                                      type="stop"))
1248                return
1249            if not numpy.isfinite(result.fitness) or \
1250                numpy.any(result.pvec == None) or not \
1251                numpy.all(numpy.isfinite(result.pvec)):
1252                self._update_fit_button(page_id)
1253                msg= "Simultaneous Fitting did not converge!!!"
1254                wx.PostEvent(self.parent, StatusEvent(status=msg,type="stop"))
1255                return
1256             
1257            for uid in page_id:   
1258                fpdict = self.page_finder[uid]
1259                for value in fpdict.itervalues():
1260                    model = value.get_model()
1261                    data =  value.get_fit_data()
1262                    small_param_name = []
1263                    small_out = []
1264                    small_cov = []
1265                    #Separate result in to data corresponding to each page
1266                    for p in result.parameters:
1267                        #print "simul ----", p , p.__class__, p.model.name, p.data.name
1268                        model_name, param_name = self.split_string(p.name) 
1269                        if model.name == model_name:
1270                            p_name= model.name+"."+param_name
1271                            if p.name == p_name:     
1272                                if p.value != None and numpy.isfinite(p.value):
1273                                    small_out.append(p.value)
1274                                    small_param_name.append(param_name)
1275                                    small_cov.append(p.stderr)
1276                # Display result on each page
1277                cpage = self.fit_panel.get_page_by_id(uid)
1278                wx.CallAfter(cpage.onsetValues, 
1279                                    result.fitness,
1280                                  small_param_name,
1281                                  small_out,small_cov)
1282                cpage._on_fit_complete()
1283                msg = "Fit completed!"
1284                wx.PostEvent(self.parent, StatusEvent(status=msg))
1285        except Exception:
1286            raise
1287            self._update_fit_button(page_id)
1288            msg = "Complex Fitting did not converge!!!"
1289            wx.PostEvent(self.parent, StatusEvent(status=msg, info="error",
1290                                                  type="stop"))
1291            return
1292        except:
1293            self._update_fit_button(page_id)
1294            msg = "Simultaneous Fit completed"
1295            msg += " but Following error occurred:%s" % sys.exc_value
1296            wx.PostEvent(self.parent, StatusEvent(status=msg, type="stop"))
1297   
1298    def _update_fit_button(self, page_id):
1299        """
1300        Update Fit button when fit stopped
1301       
1302        : parameter page_id: fitpage where the button is
1303        """
1304        if page_id.__class__.__name__ != 'list':
1305            page_id = [page_id]
1306        for uid in page_id: 
1307            page = self.fit_panel.get_page_by_id(uid)
1308            page._on_fit_complete()
1309       
1310    def _on_show_panel(self, event):
1311        """
1312        """
1313        pass
1314       
1315    def _onset_engine_park(self,event):
1316        """
1317        set engine to park
1318        """
1319        self._on_change_engine('park')
1320       
1321    def _onset_engine_scipy(self,event):
1322        """
1323        set engine to scipy
1324        """
1325        self._on_change_engine('scipy')
1326       
1327    def _on_slicer_event(self, event):
1328        """
1329        Receive a panel as event and send it to guiframe
1330       
1331        :param event: event containing a panel
1332       
1333        """
1334        if event.panel is not None:
1335            new_panel = event.panel
1336            self.slicer_panels.append(event.panel)
1337            # Set group ID if available
1338            event_id = self.parent.popup_panel(new_panel)
1339            new_panel.uid = event_id
1340            self.mypanels.append(new_panel) 
1341       
1342    def _onclearslicer(self, event):
1343        """
1344        Clear the boxslicer when close the panel associate with this slicer
1345        """
1346        name =event.GetPane().caption
1347   
1348        for panel in self.slicer_panels:
1349            if panel.window_caption==name:
1350               
1351                for item in self.parent.panels:
1352                    if hasattr(self.parent.panels[item], "uid"):
1353                        if self.parent.panels[item].uid ==panel.base.uid:
1354                            self.parent.panels[item].onClearSlicer(event)
1355                            self.parent._mgr.Update()
1356                            break 
1357                break
1358   
1359    def _return_engine_type(self):
1360        """
1361        return the current type of engine
1362        """
1363        return self._fit_engine
1364     
1365     
1366    def _on_change_engine(self, engine='park'):
1367        """
1368        Allow to select the type of engine to perform fit
1369       
1370        :param engine: the key work of the engine
1371       
1372        """
1373        ## saving fit engine name
1374        self._fit_engine = engine
1375        ## change menu item state
1376        if engine == "park":
1377            self.menu1.FindItemById(self.park_id).Check(True)
1378            self.menu1.FindItemById(self.scipy_id).Check(False)
1379        else:
1380            self.menu1.FindItemById(self.park_id).Check(False)
1381            self.menu1.FindItemById(self.scipy_id).Check(True)
1382        ## post a message to status bar
1383        msg = "Engine set to: %s" % self._fit_engine
1384        wx.PostEvent(self.parent, 
1385                     StatusEvent(status=msg))
1386        ## send the current engine type to fitpanel
1387        self.fit_panel._on_engine_change(name=self._fit_engine)
1388
1389       
1390    def _on_model_panel(self, evt):
1391        """
1392        react to model selection on any combo box or model menu.plot the model 
1393       
1394        :param evt: wx.combobox event
1395       
1396        """
1397        model = evt.model
1398        uid = evt.uid
1399        qmin = evt.qmin
1400        qmax = evt.qmax
1401        smearer = evt.smearer
1402        caption = evt.caption
1403        enable_smearer = evt.enable_smearer
1404        if model == None:
1405            return
1406        if uid not in self.page_finder.keys():
1407            return
1408        # save the name containing the data name with the appropriate model
1409        self.page_finder[uid].set_model(model)
1410        self.page_finder[uid].enable_smearing(enable_smearer)
1411        self.page_finder[uid].set_range(qmin=qmin, qmax=qmax)
1412        self.page_finder[uid].set_fit_tab_caption(caption=caption)
1413        if self.sim_page is not None and not self.batch_on:
1414            self.sim_page.draw_page()
1415       
1416    def _update1D(self, x, output):
1417        """
1418        Update the output of plotting model 1D
1419        """
1420        msg = "Plot updating ... "
1421        wx.PostEvent(self.parent, StatusEvent(status=msg,type="update"))
1422       
1423    def _complete1D(self, x, y, page_id, elapsed, index, model,
1424                    weight=None, fid=None,
1425                    toggle_mode_on=False, state=None, 
1426                    data=None, update_chisqr=True):
1427        """
1428        Complete plotting 1D data
1429        """ 
1430        try:
1431            new_plot = Data1D(x=x, y=y)
1432            new_plot.is_data = False
1433            new_plot.dy = numpy.zeros(len(y))
1434            new_plot.symbol = GUIFRAME_ID.CURVE_SYMBOL_NUM
1435            _yaxis, _yunit = data.get_yaxis() 
1436            _xaxis, _xunit = data.get_xaxis() 
1437            new_plot.title = data.name
1438
1439            new_plot.group_id = self.page_finder[page_id].get_graph_id()
1440            if new_plot.group_id == None:
1441                new_plot.group_id = data.group_id
1442            new_plot.id =  str(page_id) + "model"
1443            #if new_plot.id in self.color_dict:
1444            #    new_plot.custom_color = self.color_dict[new_plot.id]
1445            #find if this theory was already plotted and replace that plot given
1446            #the same id
1447            theory_data = self.page_finder[page_id].get_theory_data(fid=data.id)
1448            new_plot.name = model.name + " ["+ str(model.__class__.__name__)+"]"
1449            new_plot.xaxis(_xaxis, _xunit)
1450            new_plot.yaxis(_yaxis, _yunit)
1451            self.page_finder[page_id].set_theory_data(data=new_plot, 
1452                                                      fid=data.id)
1453            self.parent.update_theory(data_id=data.id, theory=new_plot,
1454                                       state=state)   
1455            current_pg = self.fit_panel.get_page_by_id(page_id)
1456            title = new_plot.title
1457            batch_on = self.fit_panel.get_page_by_id(page_id).batch_on
1458            if not batch_on:
1459                wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot,
1460                                            title=str(title)))
1461            caption = current_pg.window_caption
1462            self.page_finder[page_id].set_fit_tab_caption(caption=caption)
1463            try:
1464                # replace model cal to fit calculation if possible
1465                new_plot.y = self.page_finder[page_id].get_result(fid=data.id).theory
1466            except:
1467                pass
1468
1469            self.page_finder[page_id].set_theory_data(data=new_plot, 
1470                                                      fid=data.id)
1471            if toggle_mode_on:
1472                wx.PostEvent(self.parent, 
1473                             NewPlotEvent(group_id=str(page_id) + " Model2D",
1474                                               action="Hide"))
1475            else:
1476                if update_chisqr:
1477                    wx.PostEvent(current_pg,
1478                                 Chi2UpdateEvent(output=self._cal_chisqr(
1479                                                                data=data,
1480                                                                fid=fid,
1481                                                                weight=weight,
1482                                                            page_id=page_id,
1483                                                            index=index)))
1484                else:
1485                    self._plot_residuals(page_id=page_id, data=data, fid=fid,
1486                                          index=index, weight=weight)
1487
1488            msg = "Computation  completed!"
1489            wx.PostEvent( self.parent, StatusEvent(status=msg, type="stop" ))
1490        except:
1491            raise
1492            #msg = " Error occurred when drawing %s Model 1D: " % new_plot.name
1493            #msg += " %s"  % sys.exc_value
1494            #wx.PostEvent(self.parent, StatusEvent(status=msg, type="stop"))
1495   
1496    def _update2D(self, output,time=None):
1497        """
1498        Update the output of plotting model
1499        """
1500        wx.PostEvent(self.parent, StatusEvent(status="Plot \
1501        #updating ... ", type="update"))
1502        #self.ready_fit()
1503 
1504    def _complete2D(self, image, data, model, page_id,  elapsed, index, qmin,
1505                qmax, fid=None, weight=None, toggle_mode_on=False, state=None, 
1506                     update_chisqr=True):
1507        """
1508        Complete get the result of modelthread and create model 2D
1509        that can be plot.
1510        """
1511        new_plot= Data2D(image=image, err_image=data.err_data)
1512        new_plot.name = model.name
1513        new_plot.title = "Analytical model 2D "
1514        new_plot.id = str(page_id) + "model"
1515        new_plot.group_id = str(page_id) + " Model2D"
1516        new_plot.detector = data.detector
1517        new_plot.source = data.source
1518        new_plot.is_data = False 
1519        new_plot.qx_data = data.qx_data
1520        new_plot.qy_data = data.qy_data
1521        new_plot.q_data = data.q_data
1522        new_plot.mask = data.mask
1523        ## plot boundaries
1524        new_plot.ymin = data.ymin
1525        new_plot.ymax = data.ymax
1526        new_plot.xmin = data.xmin
1527        new_plot.xmax = data.xmax
1528        title = data.title
1529        if len(title) > 1:
1530            new_plot.title = "Model2D for " + data.name
1531        new_plot.is_data = False
1532        new_plot.name = model.name + " [" + \
1533                                    str(model.__class__.__name__) + "-2D]"
1534        theory_data = deepcopy(new_plot)
1535        theory_data.name = "Unknown"
1536       
1537        self.page_finder[page_id].set_theory_data(data=theory_data, fid=data.id)
1538        self.parent.update_theory(data_id=data.id, 
1539                                       theory=new_plot,
1540                                       state=state) 
1541        current_pg = self.fit_panel.get_page_by_id(page_id)
1542        title = new_plot.title
1543        wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot,
1544                                               title=title))
1545        try:
1546            # replace model cal to fit calculation if possible
1547            new_plot.data = self.page_finder[page_id].get_result(fid=data.id).theory
1548        except:
1549            pass
1550
1551        self.page_finder[page_id].set_theory_data(data=new_plot, fid=data.id)
1552        if toggle_mode_on:
1553            wx.PostEvent(self.parent, 
1554                             NewPlotEvent(group_id=str(page_id) + " Model1D",
1555                                               action="Hide"))
1556        else:
1557            # Chisqr in fitpage
1558            if update_chisqr:
1559                wx.PostEvent(current_pg,
1560                             Chi2UpdateEvent(output=self._cal_chisqr(data=data,
1561                                                                    weight=weight,
1562                                                                    fid=fid,
1563                                                         page_id=page_id,
1564                                                         index=index)))
1565            else:
1566                self._plot_residuals(page_id=page_id, data=data, fid=fid,
1567                                      index=index, weight=weight)
1568        msg = "Computation  completed!"
1569        wx.PostEvent(self.parent, StatusEvent(status=msg, type="stop"))
1570   
1571    def _draw_model2D(self, model, page_id, qmin,
1572                      qmax,
1573                      data=None, smearer=None,
1574                      description=None, enable2D=False,
1575                      state=None,
1576                      fid=None,
1577                      weight=None,
1578                      toggle_mode_on=False,
1579                       update_chisqr=True):
1580        """
1581        draw model in 2D
1582       
1583        :param model: instance of the model to draw
1584        :param description: the description of the model
1585        :param enable2D: when True allows to draw model 2D
1586        :param qmin: the minimum value to  draw model 2D
1587        :param qmax: the maximum value to draw model 2D
1588        :param qstep: the number of division of Qx and Qy of the model to draw
1589           
1590        """
1591        if not enable2D:
1592            return None
1593        try:
1594            from model_thread import Calc2D
1595            ## If a thread is already started, stop it
1596            if (self.calc_2D is not None) and self.calc_2D.isrunning():
1597                self.calc_2D.stop()
1598            self.calc_2D = Calc2D(model=model, 
1599                                    data=data,
1600                                    page_id=page_id,
1601                                    smearer=smearer,
1602                                    qmin=qmin,
1603                                    qmax=qmax,
1604                                    weight=weight, 
1605                                    fid=fid,
1606                                    toggle_mode_on=toggle_mode_on,
1607                                    state=state,
1608                                    completefn=self._complete2D,
1609                                    update_chisqr=update_chisqr)
1610            self.calc_2D.queue()
1611
1612        except:
1613            raise
1614            #msg = " Error occurred when drawing %s Model 2D: " % model.name
1615            #msg += " %s" % sys.exc_value
1616            #wx.PostEvent(self.parent, StatusEvent(status=msg))
1617
1618    def _draw_model1D(self, model, page_id, data, 
1619                      qmin, qmax, smearer=None,
1620                state=None,
1621                weight=None,
1622                fid=None, 
1623                toggle_mode_on=False, update_chisqr=True, 
1624                enable1D=True):
1625        """
1626        Draw model 1D from loaded data1D
1627       
1628        :param data: loaded data
1629        :param model: the model to plot
1630       
1631        """
1632        if not enable1D:
1633            return 
1634        try:
1635            from model_thread import Calc1D
1636            ## If a thread is already started, stop it
1637            if (self.calc_1D is not None) and self.calc_1D.isrunning():
1638                self.calc_1D.stop()
1639            self.calc_1D = Calc1D(data=data,
1640                                  model=model,
1641                                  page_id=page_id, 
1642                                  qmin=qmin,
1643                                  qmax=qmax,
1644                                  smearer=smearer,
1645                                  state=state,
1646                                  weight=weight,
1647                                  fid=fid,
1648                                  toggle_mode_on=toggle_mode_on,
1649                                  completefn=self._complete1D,
1650                                  #updatefn = self._update1D,
1651                                  update_chisqr=update_chisqr)
1652            self.calc_1D.queue()
1653        except:
1654            msg = " Error occurred when drawing %s Model 1D: " % model.name
1655            msg += " %s" % sys.exc_value
1656            wx.PostEvent(self.parent, StatusEvent(status=msg))
1657   
1658 
1659   
1660    def _cal_chisqr(self, page_id, data, weight, fid=None, index=None): 
1661        """
1662        Get handy Chisqr using the output from draw1D and 2D,
1663        instead of calling expansive CalcChisqr in guithread
1664        """
1665        data_copy = deepcopy(data) 
1666        # default chisqr
1667        chisqr = None
1668        #to compute chisq make sure data has valid data
1669        # return None if data == None
1670        if not check_data_validity(data_copy) or data_copy == None:
1671            return chisqr
1672
1673        # Get data: data I, theory I, and data dI in order
1674        if data_copy.__class__.__name__ == "Data2D":
1675            if index == None: 
1676                index = numpy.ones(len(data_copy.data),ntype=bool)
1677            if weight != None:
1678                data_copy.err_data = weight
1679            # get rid of zero error points
1680            index = index & (data_copy.err_data != 0) 
1681            index = index & (numpy.isfinite(data_copy.data)) 
1682            fn = data_copy.data[index] 
1683            theory_data = self.page_finder[page_id].get_theory_data(fid=data_copy.id)
1684            if theory_data== None:
1685                return chisqr
1686            gn = theory_data.data[index]
1687            en = data_copy.err_data[index]
1688        else:
1689            # 1 d theory from model_thread is only in the range of index
1690            if index == None: 
1691                index = numpy.ones(len(data_copy.y), ntype=bool)
1692            if weight != None:
1693                data_copy.dy = weight
1694            if data_copy.dy == None or data_copy.dy == []:
1695                dy = numpy.ones(len(data_copy.y))
1696            else:
1697                ## Set consitently w/AbstractFitengine:
1698                # But this should be corrected later.
1699                dy = deepcopy(data_copy.dy)
1700                dy[dy==0] = 1
1701            fn = data_copy.y[index] 
1702           
1703            theory_data = self.page_finder[page_id].get_theory_data(fid=data_copy.id)
1704            if theory_data== None:
1705                return chisqr
1706            gn = theory_data.y
1707            en = dy[index]
1708           
1709        # residual
1710        res = (fn - gn) / en
1711        residuals = res[numpy.isfinite(res)]
1712        # get chisqr only w/finite
1713        chisqr = numpy.average(residuals * residuals)
1714       
1715        self._plot_residuals(page_id=page_id, data=data_copy, 
1716                             fid=fid,
1717                             weight=weight, index=index)
1718       
1719        return chisqr
1720   
1721    def _plot_residuals(self, page_id, weight, fid=None,
1722                        data=None, index=None): 
1723        """
1724        Plot the residuals
1725       
1726        :param data: data
1727        :param index: index array (bool)
1728        : Note: this is different from the residuals in cal_chisqr()
1729        """
1730        data_copy = deepcopy(data)
1731        # Get data: data I, theory I, and data dI in order
1732        if data_copy.__class__.__name__ == "Data2D":
1733            # build residuals
1734            residuals = Data2D()
1735            #residuals.copy_from_datainfo(data)
1736            # Not for trunk the line below, instead use the line above
1737            data_copy.clone_without_data(len(data_copy.data), residuals)
1738            residuals.data = None
1739            fn = data_copy.data#[index]
1740            theory_data = self.page_finder[page_id].get_theory_data(fid=data_copy.id)
1741            gn = theory_data.data#[index]
1742            if weight == None:
1743                en = data_copy.err_data
1744            else:
1745                en = weight
1746            residuals.data = (fn - gn) / en
1747            residuals.qx_data = data_copy.qx_data#[index]
1748            residuals.qy_data = data_copy.qy_data #[index]
1749            residuals.q_data = data_copy.q_data#[index]
1750            residuals.err_data = numpy.ones(len(residuals.data))#[index]
1751            residuals.xmin = min(residuals.qx_data)
1752            residuals.xmax = max(residuals.qx_data)
1753            residuals.ymin = min(residuals.qy_data)
1754            residuals.ymax = max(residuals.qy_data)
1755            residuals.q_data = data_copy.q_data#[index]
1756            residuals.mask = data_copy.mask
1757            residuals.scale = 'linear'
1758            # check the lengths
1759            if len(residuals.data) != len(residuals.q_data):
1760                return
1761        else:
1762            # 1 d theory from model_thread is only in the range of index
1763            if data_copy.dy == None or data_copy.dy == []:
1764                dy = numpy.ones(len(data_copy.y))
1765            else:
1766                if weight == None:
1767                    dy = numpy.ones(len(data_copy.y))
1768                ## Set consitently w/AbstractFitengine:
1769                ## But this should be corrected later.
1770                else:
1771                    dy = weight#deepcopy(data_copy.dy)
1772                dy[dy==0] = 1 
1773            fn = data_copy.y[index] 
1774            theory_data = self.page_finder[page_id].get_theory_data(fid=data_copy.id)
1775            gn = theory_data.y
1776            en = dy[index]
1777            # build residuals
1778            residuals = Data1D()
1779            residuals.y = (fn - gn) / en
1780            residuals.x = data_copy.x[index]
1781            residuals.dy = numpy.ones(len(residuals.y))
1782            residuals.dx = None
1783            residuals.dxl = None
1784            residuals.dxw = None
1785            residuals.ytransform = 'y'
1786            # For latter scale changes
1787            residuals.xaxis('\\rm{Q} ', 'A^{-1}')
1788            residuals.yaxis('\\rm{Residuals} ', 'normalized')
1789        new_plot = residuals
1790        new_plot.name = "Residuals for " + str(data.name)
1791        ## allow to highlight data when plotted
1792        new_plot.interactive = True
1793        ## when 2 data have the same id override the 1 st plotted
1794        new_plot.id = "res" + str(data_copy.id)#name + " residuals"
1795        ##group_id specify on which panel to plot this data
1796        group_id = self.page_finder[page_id].get_graph_id()
1797        if group_id == None:
1798            group_id = data.group_id
1799        new_plot.group_id ="res" + str(group_id)
1800        #new_plot.is_data = True
1801        ##post data to plot
1802        title = new_plot.name
1803        self.page_finder[page_id].set_residuals(residuals=new_plot, fid=data.id)
1804        batch_on = self.fit_panel.get_page_by_id(page_id).batch_on
1805        if not batch_on:
1806            wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot, title=title))
1807            return
1808        #reset weight 
1809        #self.weight = None
1810        # Need all residuals before plotting
1811        # Should be refactored
1812        n = len(self.page_finder[page_id].keys())
1813        m = self.page_finder[page_id].nbr_residuals_computed
1814        flag = False
1815        batch_inputs, batch_outputs = self.page_finder[page_id].get_batch_result()
1816         
1817        if self.page_finder[page_id].nbr_residuals_computed == -1:
1818            flag = False
1819        else:
1820            if m == n -1:
1821                flag = True
1822            else:
1823                flag = False
1824            self.page_finder[page_id].nbr_residuals_computed += 1
1825           
1826            self.on_set_batch_result(page_id=page_id,
1827                                 fid=fid,
1828                              batch_outputs=batch_outputs, 
1829                                batch_inputs=batch_inputs) 
1830        # plot data
1831        event = BatchDrawEvent(page_id=page_id , 
1832                           batch_on=batch_on,
1833                           fid=fid,
1834                           batch_outputs=batch_outputs,
1835                           batch_inputs=batch_inputs,
1836                           is_displayed=flag)
1837        wx.PostEvent(self.parent, event)
1838       
1839
1840       
1841    def on_display_grid(self, event):
1842        """
1843        deplay batch result
1844        """
1845        fid = event.fid
1846        page_id = event.page_id
1847        batch_on = event.batch_on
1848        flag = event.is_displayed
1849        batch_outputs = event.batch_outputs
1850        batch_inputs = event.batch_inputs
1851        if flag and batch_on and fid is not None:
1852            self.parent.on_set_batch_result(data_outputs=batch_outputs, 
1853                                            data_inputs=batch_inputs,
1854                                            plugin_name=self.sub_menu)
1855            self.page_finder[page_id].nbr_residuals_computed = -1
1856       
1857#def profile(fn, *args, **kw):
1858#    import cProfile, pstats, os
1859#    global call_result
1860#    def call():
1861#        global call_result
1862#        call_result = fn(*args, **kw)
1863#    cProfile.runctx('call()', dict(call=call), {}, 'profile.out')
1864#    stats = pstats.Stats('profile.out')
1865#    #stats.sort_stats('time')
1866#    stats.sort_stats('calls')
1867#    stats.print_stats()
1868#    os.unlink('profile.out')
1869#    return call_result
1870if __name__ == "__main__":
1871    i = Plugin()
1872   
1873   
1874   
1875   
Note: See TracBrowser for help on using the repository browser.