source: sasview/fittingview/src/sans/perspectives/fitting/fitting.py @ 26fc866

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

fixed park & batch switching param value problem

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