source: sasview/fittingview/src/sans/perspectives/fitting/fitting.py @ 581d1d4

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

set as top window and setfocus on simulpage when accessing from the menubar

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