source: sasview/fittingview/src/sans/perspectives/fitting/fitting.py @ 33dd2e5

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

working on batch

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