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

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 d4edd72 was d4edd72, checked in by Jessica Tumarkin <jtumarki@…>, 13 years ago

attempted to fix color issues

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