source: sasview/fittingview/src/sans/perspectives/fitting/fitting.py @ 6446f87

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

working on batch

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