source: sasview/sansview/perspectives/fitting/fitting.py @ e2f0554

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 e2f0554 was 8b6f489, checked in by Gervaise Alina <gervyh@…>, 14 years ago

working on toggle 1d to 2 d

  • Property mode set to 100644
File size: 53.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
19#import math
20import string
21import time
22#import thread
23from copy import deepcopy
24
25import models
26import fitpage
27
28from DataLoader.loader import Loader
29from sans.guiframe.dataFitting import Data2D
30from sans.guiframe.dataFitting import Data1D
31from sans.guiframe.events import NewPlotEvent
32from sans.guiframe.events import StatusEvent 
33from sans.guiframe.events import EVT_SLICER_PANEL
34from sans.guiframe.events import EVT_REMOVE_DATA
35from sans.guiframe.events import EVT_SLICER_PARS_UPDATE
36from sans.guiframe.gui_style import GUIFRAME_ID
37from sans.guiframe.plugin_base import PluginBase
38
39
40from .console import ConsoleUpdate
41from .fitproblem import FitProblem
42from .fitpanel import FitPanel
43from .fit_thread import FitThread
44from .pagestate import Reader
45from .fitpage import Chi2UpdateEvent
46
47DEFAULT_BEAM = 0.005
48DEFAULT_QMIN = 0.001
49DEFAULT_QMAX = 0.13
50DEFAULT_NPTS = 50
51MAX_NBR_DATA = 4
52
53
54(PageInfoEvent, EVT_PAGE_INFO)   = wx.lib.newevent.NewEvent()
55
56   
57
58class Plugin(PluginBase):
59    """
60    Fitting plugin is used to perform fit
61    """
62    def __init__(self, standalone=False):
63        PluginBase.__init__(self, name="Fitting", standalone=standalone)
64       
65        #Provide list of models existing in the application
66        self.menu_mng = models.ModelManager()
67       
68        #list of panel to send to guiframe
69        self.mypanels = []
70        # reference to the current running thread
71        self.calc_2D = None
72        self.calc_1D = None
73        self.calc_fit = None
74       
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        #let fit ready
80        self.fitproblem_count = None
81        #Flag to let the plug-in know that it is running stand alone
82        self.standalone = True
83        ## dictionary of page closed and id
84        self.closed_page_dict = {}
85        ## Fit engine
86        self._fit_engine = 'scipy'
87        #List of selected data
88        self.selected_data_list = []
89        ## list of slicer panel created to display slicer parameters and results
90        self.slicer_panels = []
91        # model 2D view
92        self.model2D_id = None
93        #keep reference of the simultaneous fit page
94        self.sim_page = None
95   
96        #Create a reader for fit page's state
97        self.state_reader = None 
98        self._extensions = '.fitv'
99        self.temp_state = []
100        self.state_index = 0
101        self.sfile_ext = None
102        # take care of saving  data, model and page associated with each other
103        self.page_finder = {}
104        # Log startup
105        logging.info("Fitting plug-in started") 
106       
107    def populate_menu(self, owner):
108        """
109        Create a menu for the Fitting plug-in
110       
111        :param id: id to create a menu
112        :param owner: owner of menu
113       
114        :return: list of information to populate the main menu
115       
116        """
117        #Menu for fitting
118        self.menu1 = wx.Menu()
119       
120        #Set park engine
121        id3 = wx.NewId()
122        scipy_help= "Scipy Engine: Perform Simple fit. More in Help window...."
123        self.menu1.AppendCheckItem(id3, "Simple Fit  [Scipy]",scipy_help) 
124        wx.EVT_MENU(owner, id3,  self._onset_engine_scipy)
125       
126        id3 = wx.NewId()
127        park_help = "Park Engine: Perform Complex fit. More in Help window...."
128        self.menu1.AppendCheckItem(id3, "Complex Fit  [Park]",park_help) 
129        wx.EVT_MENU(owner, id3,  self._onset_engine_park)
130       
131        self.menu1.FindItemByPosition(0).Check(True)
132        self.menu1.FindItemByPosition(1).Check(False)
133           
134        self.menu1.AppendSeparator()
135        id1 = wx.NewId()
136        simul_help = "Simultaneous Fit"
137        self.menu1.Append(id1, '&Simultaneous Page',simul_help)
138        wx.EVT_MENU(owner, id1, self.on_add_sim_page)
139       
140        id1 = wx.NewId()
141        simul_help = "Add new fit page"
142        self.menu1.Append(id1, '&Create New Page',simul_help)
143        wx.EVT_MENU(owner, id1, self.on_add_new_page)
144       
145        #self.fit_panel.set_model_list(self.menu_mng.get_model_list())
146   
147        #create  menubar items
148        return [(self.menu1, "Fitting")]
149               
150    def on_add_sim_page(self, event):
151        """
152        Create a page to access simultaneous fit option
153        """
154        if self.sim_page != None:
155            msg= "Simultaneous Fit page already opened"
156            wx.PostEvent(self.parent, StatusEvent(status= msg))
157            return 
158       
159        self.sim_page= self.fit_panel.add_sim_page()
160       
161    def help(self, evt):
162        """
163        Show a general help dialog.
164        """
165        from help_panel import  HelpWindow
166        frame = HelpWindow(None, -1, 'HelpWindow')   
167        frame.Show(True)
168       
169    def get_context_menu(self, plotpanel=None):
170        """
171        Get the context menu items available for P(r).them allow fitting option
172        for Data2D and Data1D only.
173       
174        :param graph: the Graph object to which we attach the context menu
175       
176        :return: a list of menu items with call-back function
177       
178        :note: if Data1D was generated from Theory1D 
179                the fitting option is not allowed
180               
181        """
182        graph = plotpanel.graph
183        fit_option = "Select data for fitting"
184        fit_hint =  "Dialog with fitting parameters "
185       
186        if graph.selected_plottable not in plotpanel.plots:
187            return []
188        item = plotpanel.plots[graph.selected_plottable]
189        if item.__class__.__name__ is "Data2D": 
190            if hasattr(item,"is_data"):
191                if item.is_data:
192                    return [[fit_option, fit_hint, self._onSelect]]
193                else:
194                    return [] 
195            return [[fit_option, fit_hint, self._onSelect]]
196        else:
197           
198            # if is_data is true , this in an actual data loaded
199            #else it is a data created from a theory model
200            if hasattr(item,"is_data"):
201                if item.is_data:
202                    return [[fit_option, fit_hint,
203                              self._onSelect]]
204                else:
205                    return [] 
206        return []   
207
208
209    def get_panels(self, parent):
210        """
211        Create and return a list of panel objects
212        """
213        self.parent = parent
214        #self.parent.Bind(EVT_FITSTATE_UPDATE, self.on_set_state_helper)
215        # Creation of the fit panel
216        self.fit_panel = FitPanel(parent=self.parent, manager=self)
217        self.on_add_new_page(event=None)
218        #Set the manager for the main panel
219        self.fit_panel.set_manager(self)
220        # List of windows used for the perspective
221        self.perspective = []
222        self.perspective.append(self.fit_panel.window_name)
223       
224        #index number to create random model name
225        self.index_model = 0
226        self.index_theory= 0
227        self.parent.Bind(EVT_SLICER_PANEL, self._on_slicer_event)
228       
229        self.parent.Bind(EVT_REMOVE_DATA, self._closed_fitpage)
230        self.parent.Bind(EVT_SLICER_PARS_UPDATE, self._onEVT_SLICER_PANEL)
231        self.parent._mgr.Bind(wx.aui.EVT_AUI_PANE_CLOSE,self._onclearslicer)   
232        #Create reader when fitting panel are created
233        self.state_reader = Reader(self.set_state)   
234        #append that reader to list of available reader
235        loader = Loader()
236        loader.associate_file_reader(".fitv", self.state_reader)
237        loader.associate_file_reader(".svs", self.state_reader)
238        from sans.perspectives.calculator.sld_panel import SldPanel
239        #Send the fitting panel to guiframe
240        self.mypanels.append(self.fit_panel) 
241        self.mypanels.append(SldPanel(parent=self.parent, base=self.parent))
242        return self.mypanels
243   
244    def clear_panel(self):
245        """
246        """
247        self.fit_panel.clear_panel()
248       
249    def set_default_perspective(self):
250        """
251        Call back method that True to notify the parent that the current plug-in
252        can be set as default  perspective.
253        when returning False, the plug-in is not candidate for an automatic
254        default perspective setting
255        """
256        return True
257   
258    def set_data(self, data_list):
259        """
260        receive a list of data to fit
261        """
262        selected_data_list = []
263        if len(data_list) > MAX_NBR_DATA :
264            from fitting_widgets import DataDialog
265            dlg = DataDialog(data_list=data_list, nb_data=MAX_NBR_DATA)
266            if dlg.ShowModal() == wx.ID_OK:
267                selected_data_list = dlg.get_data()
268        else:
269            selected_data_list = data_list
270        for data in selected_data_list:
271            self.add_fit_page(data=data)
272            wx.PostEvent(self.parent, NewPlotEvent(plot=data, 
273                                                   title=str(data.title)))
274           
275    def set_state(self, state=None, datainfo=None, format=None):
276        """
277        Call-back method for the fit page state reader.
278        This method is called when a .fitv/.svs file is loaded.
279       
280        : param state: PageState object
281        : param datainfo: data
282        """
283        #state = self.state_reader.get_state()
284        if state != None:
285            # store fitting state in temp_state
286            self.temp_state.append(state) 
287        else:
288            self.temp_state = []
289        # index to start with for a new set_state
290        self.state_index = 0
291        # state file format
292        self.sfile_ext = format
293       
294        self.on_set_state_helper(event=None)
295
296    def  on_set_state_helper(self,event=None):
297        """
298        Set_state_helper. This actually sets state after plotting data from state file.
299       
300        : event: FitStateUpdateEvent called by dataloader.plot_data from guiframe
301        """
302        if len(self.temp_state) == 0:
303            if self.state_index==0 and len(self.mypanels) <= 0 and self.sfile_ext =='.svs':
304                self.fit_panel.add_default_pages()
305                self.temp_state = []
306                self.state_index = 0
307            return
308       
309        try:
310            # Load fitting state
311            state = self.temp_state[self.state_index]
312            #panel state should have model selection to set_state
313            if state.formfactorcombobox != None:
314                #set state
315                data = self.parent.create_gui_data(state.data)
316               
317                data.group_id = state.data.group_id
318                self.parent.add_data(data_list=[data])
319                wx.PostEvent(self.parent, NewPlotEvent(plot=data,
320                                        title=data.title))
321                page = self.fit_panel.set_state(state)   
322            else:
323                self.parent.add_data(data_list=[data])
324                wx.PostEvent(self.parent, NewPlotEvent(plot=data,
325                                        title=data.title))
326                #just set data because set_state won't work
327                data = self.parent.create_gui_data(state.data)
328                data.group_id = state.data.group_id
329                self.add_fit_page(data)
330                caption = panel.window_name
331                self.store_data(page=panel.id, data=data, caption=caption)
332                self.mypanels.append(panel) 
333               
334            # get ready for the next set_state
335            self.state_index += 1
336
337            #reset state variables to default when all set_state is finished.
338            if len(self.temp_state) == self.state_index:
339               
340                self.temp_state = []
341                #self.state_index = 0
342                # Make sure the user sees the fitting panel after loading
343                #self.parent.set_perspective(self.perspective)
344                self.on_perspective(event=None)
345        except:
346            self.state_index==0
347            self.temp_state = []
348            raise
349                 
350    def save_fit_state(self, filepath, fitstate): 
351        """
352        save fit page state into file
353        """
354        self.state_reader.write(filename=filepath, fitstate=fitstate)
355       
356    def set_fit_range(self, id, qmin, qmax):
357        """
358        Set the fitting range of a given page
359        """
360        self.page_finder[id].set_range(qmin=qmin, qmax=qmax)
361                   
362    def schedule_for_fit(self,value=0, id=None,fitproblem =None): 
363        """
364        Set the fit problem field to 0 or 1 to schedule that problem to fit.
365        Schedule the specified fitproblem or get the fit problem related to
366        the current page and set value.
367       
368        :param value: integer 0 or 1
369        :param fitproblem: fitproblem to schedule or not to fit
370       
371        """   
372        if fitproblem !=None:
373            fitproblem.schedule_tofit(value)
374        else:
375            self.page_finder[id].schedule_tofit(value)
376         
377    def get_page_finder(self):
378        """
379        return self.page_finder used also by simfitpage.py
380        """ 
381        return self.page_finder
382   
383    def set_page_finder(self,modelname,names,values):
384        """
385        Used by simfitpage.py to reset a parameter given the string constrainst.
386         
387        :param modelname: the name ot the model for with the parameter has to reset
388        :param value: can be a string in this case.
389        :param names: the paramter name
390         
391        :note: expecting park used for fit.
392         
393        """ 
394        sim_page_id = self.sim_page.id
395        for id, value in self.page_finder.iteritems():
396            if id != sim_page_id:
397                list = value.get_model()
398                model = list[0]
399                if model.name == modelname:
400                    value.set_model_param(names, values)
401                    break
402         
403    def split_string(self,item): 
404        """
405        receive a word containing dot and split it. used to split parameterset
406        name into model name and parameter name example: ::
407       
408            paramaterset (item) = M1.A
409            Will return model_name = M1 , parameter name = A
410           
411        """
412        if string.find(item,".")!=-1:
413            param_names= re.split("\.",item)
414            model_name=param_names[0]           
415            ##Assume max len is 3; eg., M0.radius.width
416            if len(param_names) == 3:
417                param_name=param_names[1]+"."+param_names[2]
418            else:
419                param_name=param_names[1]                   
420            return model_name,param_name
421       
422    def stop_fit(self):
423        """
424        Stop the fit engine
425        """
426        if self.calc_fit!= None and self.calc_fit.isrunning():
427            self.calc_fit.stop()
428            wx.PostEvent(self.parent, StatusEvent(status="Fitting  \
429                is cancelled" , type="stop"))
430 
431    def set_smearer(self, id, smearer, qmin=None, qmax=None, draw=True):
432        """
433        Get a smear object and store it to a fit problem
434       
435        :param smearer: smear object to allow smearing data
436       
437        """   
438        if id not in self.page_finder.keys():
439            msg = "Cannot find ID: %s in page_finder" % str(id)
440            raise ValueError, msg
441        self.page_finder[id].set_smearer(smearer)
442        if draw:
443            ## draw model 1D with smeared data
444            data =  self.page_finder[id].get_fit_data()
445            model = self.page_finder[id].get_model()
446            if model is None:
447                return
448            ## if user has already selected a model to plot
449            ## redraw the model with data smeared
450            smear = self.page_finder[id].get_smearer()
451            self.draw_model(model=model, data=data, id=id, smearer=smear,
452                qmin=qmin, qmax=qmax)
453
454    def draw_model(self, model, id, data=None, smearer=None,
455                   enable1D=True, enable2D=False,
456                   state=None,
457                   qmin=DEFAULT_QMIN, qmax=DEFAULT_QMAX, qstep=DEFAULT_NPTS):
458        """
459        Draw model.
460       
461        :param model: the model to draw
462        :param name: the name of the model to draw
463        :param data: the data on which the model is based to be drawn
464        :param description: model's description
465        :param enable1D: if true enable drawing model 1D
466        :param enable2D: if true enable drawing model 2D
467        :param qmin:  Range's minimum value to draw model
468        :param qmax:  Range's maximum value to draw model
469        :param qstep: number of step to divide the x and y-axis
470             
471        """
472        if data.__class__.__name__ == "Data1D" or not enable2D:   
473            ## draw model 1D with no loaded data
474           
475            self._draw_model1D(model=model, 
476                               data=data,
477                               id=id,
478                               enable1D=enable1D, 
479                               smearer=smearer,
480                               qmin=qmin,
481                               qmax=qmax, 
482                               state=state,
483                               qstep=qstep)
484        else:     
485            ## draw model 2D with no initial data
486             self._draw_model2D(model=model,
487                                id=id,
488                                data=data,
489                                enable2D=enable2D,
490                                smearer=smearer,
491                                qmin=qmin,
492                                qmax=qmax,
493                                state=state,
494                                qstep=qstep)
495           
496    def onFit(self):
497        """
498        perform fit
499        """
500        ##  count the number of fitproblem schedule to fit
501        fitproblem_count = 0
502        for value in self.page_finder.itervalues():
503            if value.get_scheduled() == 1:
504                fitproblem_count += 1
505               
506        ## if simultaneous fit change automatically the engine to park
507        if fitproblem_count > 1:
508            self._on_change_engine(engine='park')
509           
510        self.fitproblem_count = fitproblem_count 
511         
512        from sans.fit.Fitting import Fit
513        self.fitter = Fit(self._fit_engine)
514       
515        if self._fit_engine == "park":
516            engineType = "Simultaneous Fit"
517        else:
518            engineType = "Single Fit"
519           
520        fproblemId = 0
521        self.current_pg = None
522        for id, value in self.page_finder.iteritems():
523            try:
524                if value.get_scheduled() == 1:
525                    #Get list of parameters name to fit
526                    pars = []
527                    templist = []
528                    page = self.fit_panel.get_page_by_id(id)
529                    templist = page.get_param_list()
530                    for element in templist:
531                        name = str(element[1])
532                        pars.append(name)
533                    #Set Engine  (model , data) related to the page on
534                    self._fit_helper(value=value, pars=pars,
535                                      id=fproblemId, title=engineType) 
536                    fproblemId += 1 
537                    self.current_page_id = page
538            except:
539                raise
540                #msg= "%s error: %s" % (engineType, sys.exc_value)
541                #wx.PostEvent(self.parent, StatusEvent(status=msg, info="error",
542                #                                      type="stop"))
543                #return
544        ## If a thread is already started, stop it
545        #if self.calc_fit!= None and self.calc_fit.isrunning():
546        #    self.calc_fit.stop()
547         #Handler used for park engine displayed message
548        handler = ConsoleUpdate(parent=self.parent,improvement_delta=0.1)
549        ## perform single fit
550        if fitproblem_count == 1:
551            calc_fit = FitThread(parent=self.parent,
552                                    handler = handler,
553                                    fn=self.fitter,
554                                   pars=pars,
555                                   updatefn=handler.update_fit,
556                                   completefn=self._single_fit_completed)
557        else:
558            ## Perform more than 1 fit at the time
559            calc_fit = FitThread(parent=self.parent,
560                                handler=handler,
561                                    fn= self.fitter,
562                                   completefn= self._simul_fit_completed,
563                                  updatefn=handler.update_fit)
564       
565        calc_fit.queue()
566        self.ready_fit(calc_fit=calc_fit)
567     
568    def ready_fit(self, calc_fit):
569        """
570        Ready for another fit
571        """
572        if self.fitproblem_count != None and self.fitproblem_count > 1:
573            calc_fit.ready(2.5)
574           
575        else:
576            time.sleep(0.4)
577           
578    def remove_plot(self, id, theory=False):
579        """
580        remove model plot when a fit page is closed
581        """
582        fitproblem = self.page_finder[id]
583        data = fitproblem.get_fit_data()
584        model = fitproblem.get_model()
585        id = None
586        if model is not None:
587            id = data.id + name
588        if theory:
589            id = data.id
590        group_id = data.group_id[len(data.group_id)-1]
591        wx.PostEvent(self.parent, NewPlotEvent(id=id,
592                                                   group_id=group_id,
593                                                   remove=True))
594           
595    def store_data(self, id, data=None, caption=None):
596        """
597        Helper to save page reference into the plug-in
598       
599        :param page: page to store
600       
601        """
602        #create a fitproblem storing all link to data,model,page creation
603        if not id in self.page_finder.keys():
604            self.page_finder[id] = FitProblem()
605        self.page_finder[id].set_fit_data(data)
606        self.page_finder[id].set_fit_tab_caption(caption)
607       
608    def on_add_new_page(self, event=None):
609        """
610        ask fit panel to create a new empty page
611        """
612        try:
613            page = self.fit_panel.add_empty_page()
614            page_caption = page.window_name
615            # add data associated to the page created
616            if page != None: 
617                self.store_data(id=page.id, caption=page_caption,
618                                data=page.get_data())
619                wx.PostEvent(self.parent, StatusEvent(status="Page Created",
620                                               info="info"))
621            else:
622                msg = "Page was already Created"
623                wx.PostEvent(self.parent, StatusEvent(status=msg,
624                                                       info="warning"))
625        except:
626            raise
627            #msg = "Creating Fit page: %s"%sys.exc_value
628            #wx.PostEvent(self.parent, StatusEvent(status=msg, info="error"))
629       
630       
631    def add_fit_page(self, data):
632        """
633        given a data, ask to the fitting panel to create a new fitting page,
634        get this page and store it into the page_finder of this plug-in
635        """
636       
637        page = self.fit_panel.set_data(data)
638        page_caption = page.window_name
639        #append Data1D to the panel containing its theory
640        #if theory already plotted
641       
642        if page.id in self.page_finder:
643            theory_data = self.page_finder[page.id].get_theory_data()
644            if issubclass(data.__class__, Data2D):
645                data.group_id.append(wx.NewId())
646            else:
647                if theory_data is not None:
648                    group_id = theory_data.group_id[len(theory_data.group_id)-1]
649                    if group_id not in data.group_id:
650                        data.group_id.append(group_id)
651            self.parent.update_data(prev_data=theory_data, new_data=data)
652                   
653        self.store_data(id=page.id, data=data, caption=page.window_name)
654        if self.sim_page is not None:
655            self.sim_page.draw_page()
656           
657    def _onEVT_SLICER_PANEL(self, event):
658        """
659        receive and event telling to update a panel with a name starting with
660        event.panel_name. this method update slicer panel for a given interactor.
661       
662        :param event: contains type of slicer , paramaters for updating the panel
663            and panel_name to find the slicer 's panel concerned.
664        """
665        for item in self.parent.panels:
666            if self.parent.panels[item].window_caption.startswith(event.panel_name):
667                self.parent.panels[item].set_slicer(event.type, event.params)
668               
669        self.parent._mgr.Update()
670   
671    def _closed_fitpage(self, event):   
672        """
673        request fitpanel to close a given page when its unique data is removed
674        from the plot. close fitpage only when the a loaded data is removed
675        """   
676        if event is None or event.data is None:
677            return
678       
679        if hasattr(event.data,"is_data"):
680            if not event.data.is_data or \
681                event.data.__class__.__name__=="Data1D":
682                self.fit_panel.close_page_with_data(event.data) 
683       
684    def _add_page_onmenu(self, name,fitproblem=None):
685        """
686        Add name of a closed page of fitpanel in a menu
687        """
688        list = self.menu1.GetMenuItems()
689        for item in list:
690            if name == item.GetItemLabel():
691                self.closed_page_dict[name][1] = fitproblem
692               
693        if not name in self.closed_page_dict.keys():   
694            # Post paramters
695            event_id = wx.NewId()
696            self.menu1.Append(event_id, name, "Show %s fit panel" % name)
697            self.closed_page_dict[name]= [event_id, fitproblem]
698            wx.EVT_MENU(self.parent,event_id,  self._open_closed_page)
699       
700    def _open_closed_page(self, event):   
701        """
702        reopen a closed page
703        """
704        for name, value in self.closed_page_dict.iteritems():
705            if event.GetId() in value:
706                id,fitproblem = value
707                if name !="Model":
708                    data= fitproblem.get_fit_data()
709                    page = self.fit_panel.add_fit_page(data= data,reset=True)
710                    if fitproblem != None:
711                        self.page_finder[id] = fitproblem
712                        if self.sim_page != None:
713                            self.sim_page.draw_page()
714                           
715                else:
716                    model = fitproblem
717                    self.fit_panel.add_model_page(model=model, topmenu=True,
718                                                  reset= True)
719                    break
720   
721    def _reset_schedule_problem(self, id, value=0):
722        """
723        unschedule or schedule all fitproblem to be fit
724        """
725        for page_id in self.page_finder.keys():
726            self.page_finder[id].schedule_tofit(value)
727           
728    def _fit_helper(self, pars, value, id, title="Single Fit " ):
729        """
730        helper for fitting
731        """
732        metadata = value.get_fit_data()
733        model = value.get_model()
734        smearer = value.get_smearer()
735        qmin, qmax = value.get_range()
736        self.fit_id = id
737        #Create list of parameters for fitting used
738        templist = []
739       
740        try:
741            #Extra list of parameters and their constraints
742            listOfConstraint = []
743           
744            param = value.get_model_param()
745            if len(param) > 0:
746                for item in param:
747                    ## check if constraint
748                    if item[0] != None and item[1] != None:
749                        listOfConstraint.append((item[0],item[1]))
750                   
751            #Do the single fit
752            self.fitter.set_model(model, self.fit_id,
753                                   pars, constraints=listOfConstraint)
754           
755            self.fitter.set_data(data=metadata, id=self.fit_id,
756                                 smearer=smearer, qmin=qmin, qmax=qmax)
757           
758            self.fitter.select_problem_for_fit(id=self.fit_id,
759                                               value=value.get_scheduled())
760            value.clear_model_param()
761        except:
762            raise
763            #msg = title + " error: %s" % sys.exc_value
764            #wx.PostEvent(self.parent, StatusEvent(status=msg, type="stop"))
765         
766    def _onSelect(self,event):
767        """
768        when Select data to fit a new page is created .Its reference is
769        added to self.page_finder
770        """
771        self.panel = event.GetEventObject()
772        Plugin.on_perspective(self, event=event)
773        for plottable in self.panel.graph.plottables:
774            if plottable.__class__.__name__ in ["Data1D", "Theory1D"]:
775                if plottable.name == self.panel.graph.selected_plottable:
776                    data = plottable
777                    self.add_fit_page(data=data)
778                    return
779            else:
780                data = plottable
781                self.add_fit_page(data=data)
782           
783    def _single_fit_completed(self,result, pars, elapsed=None):
784        """
785        Display fit result on one page of the notebook.
786       
787        :param result: result of fit
788        :param pars: list of names of parameters fitted
789        :param current_pg: the page where information will be displayed
790        :param qmin: the minimum value of x to replot the model
791        :param qmax: the maximum value of x to replot model
792         
793        """     
794        try:
795            if result ==None:
796                msg= "Simple Fitting Stop !!!"
797                wx.PostEvent(self.parent, StatusEvent(status=msg,info="warning",
798                                                      type="stop"))
799                return
800            if not numpy.isfinite(result.fitness) or \
801                    numpy.any(result.pvec == None) or \
802                    not numpy.all(numpy.isfinite(result.pvec)):
803                msg = "Single Fitting did not converge!!!"
804                wx.PostEvent(self.parent, 
805                             StatusEvent(status=msg, type="stop"))
806                return
807            page_id = None
808            for id, value in self.page_finder.iteritems():   
809                if value.get_scheduled()==1:
810                    model = value.get_model()
811                    page_id = id
812                    break
813            param_name = []
814            i = 0
815            for name in pars:
816                param_name.append(name)
817               
818            cpage = self.fit_panel.get_page_by_id(page_id)
819            cpage.onsetValues(result.fitness, 
820                              param_name, result.pvec,result.stderr)
821           
822        except:
823            msg = "Single Fit completed but Following"
824            msg += " error occurred:%s" % sys.exc_value
825            wx.PostEvent(self.parent, StatusEvent(status=msg, info="error",
826                                                  type="stop"))
827            return
828       
829    def _simul_fit_completed(self, result, pars=None, elapsed=None):
830        """
831        Parameter estimation completed,
832        display the results to the user
833       
834        :param alpha: estimated best alpha
835        :param elapsed: computation time
836       
837        """
838        ## fit more than 1 model at the same time
839        try:
840            msg = "" 
841            if result ==None:
842                msg= "Complex Fitting Stop !!!"
843                wx.PostEvent(self.parent, StatusEvent(status=msg,type="stop"))
844                return
845            if not numpy.isfinite(result.fitness) or numpy.any(result.pvec ==None )or not numpy.all(numpy.isfinite(result.pvec) ):
846                msg= "Simultaneous Fitting did not converge!!!"
847                wx.PostEvent(self.parent, StatusEvent(status=msg,type="stop"))
848                return
849             
850            for id, value in self.page_finder.iteritems():   
851                if value.get_scheduled() == 1:
852                    model = value.get_model()
853                    data =  value.get_fit_data()
854                    small_param_name = []
855                    small_out = []
856                    small_cov = []
857                    i = 0
858                    #Separate result in to data corresponding to each page
859                    for p in result.parameters:
860                        model_name,param_name = self.split_string(p.name) 
861                        if model.name == model_name:
862                            p_name= model.name+"."+param_name
863                            if p.name == p_name:     
864                                if p.value != None and numpy.isfinite(p.value):
865                                    small_out.append(p.value )
866                                    small_param_name.append(param_name)
867                                    small_cov.append(p.stderr)
868
869                    # Display result on each page
870                    cpage = self.fit_panel.get_page_by_id(id)
871                    cpage.onsetValues(result.fitness,
872                                      small_param_name,
873                                      small_out,small_cov)
874        except:
875            raise
876            #msg= "Simultaneous Fit completed"
877            #msg +=" but Following error occurred:%s"%sys.exc_value
878            #wx.PostEvent(self.parent, StatusEvent(status=msg,type="stop"))
879            #return
880             
881    def _on_show_panel(self, event):
882        """
883        """
884        pass
885       
886    def _onset_engine_park(self,event):
887        """
888        set engine to park
889        """
890        self._on_change_engine('park')
891       
892    def _onset_engine_scipy(self,event):
893        """
894        set engine to scipy
895        """
896        self._on_change_engine('scipy')
897       
898    def _on_slicer_event(self, event):
899        """
900        Receive a panel as event and send it to guiframe
901       
902        :param event: event containing a panel
903       
904        """
905        if event.panel is not None:
906            new_panel = event.panel
907            self.slicer_panels.append(event.panel)
908            # Set group ID if available
909            event_id = self.parent.popup_panel(new_panel)
910            #self.menu3.Append(event_id, new_panel.window_caption,
911            #                 "Show %s plot panel" % new_panel.window_caption)
912            # Set id to allow us to reference the panel later
913         
914            new_panel.uid = event_id
915            self.mypanels.append(new_panel) 
916       
917    def _onclearslicer(self, event):
918        """
919        Clear the boxslicer when close the panel associate with this slicer
920        """
921        name =event.GetPane().caption
922   
923        for panel in self.slicer_panels:
924            if panel.window_caption==name:
925               
926                for item in self.parent.panels:
927                    if hasattr(self.parent.panels[item],"uid"):
928                        if self.parent.panels[item].uid ==panel.base.uid:
929                            self.parent.panels[item].onClearSlicer(event)
930                            self.parent._mgr.Update()
931                            break 
932                break
933   
934    def _return_engine_type(self):
935        """
936        return the current type of engine
937        """
938        return self._fit_engine
939     
940     
941    def _on_change_engine(self, engine='park'):
942        """
943        Allow to select the type of engine to perform fit
944       
945        :param engine: the key work of the engine
946       
947        """
948        ## saving fit engine name
949        self._fit_engine = engine
950        ## change menu item state
951        if engine=="park":
952            self.menu1.FindItemByPosition(0).Check(False)
953            self.menu1.FindItemByPosition(1).Check(True)
954        else:
955            self.menu1.FindItemByPosition(0).Check(True)
956            self.menu1.FindItemByPosition(1).Check(False)
957        ## post a message to status bar
958        msg = "Engine set to: %s" % self._fit_engine
959        wx.PostEvent(self.parent, 
960                     StatusEvent(status=msg))
961        ## Bind every open fit page with a newevent to
962        #know the current fitting engine
963        #import fitpage
964        event = fitpage.FitterTypeEvent()
965        event.type = self._fit_engine
966        wx.PostEvent(self.fit_panel, event)
967       
968    def _on_model_panel(self, evt):
969        """
970        react to model selection on any combo box or model menu.plot the model 
971       
972        :param evt: wx.combobox event
973       
974        """
975        model = evt.model
976        id = evt.id
977        qmin = evt.qmin
978        qmax = evt.qmax
979        smearer = evt.smearer
980        if model == None:
981            return
982       
983        if self.page_finder[id].get_model() is None:
984            model.name = "M" + str(self.index_model)
985            self.index_model += 1 
986        else:
987            model.name = self.page_finder[id].get_model().name
988        # save the name containing the data name with the appropriate model
989        self.page_finder[id].set_model(model)
990        self.page_finder[id].set_range(qmin=qmin, qmax=qmax)
991        if self.sim_page is not None:
992            self.sim_page.draw_page()
993       
994    def _update1D(self,x, output):
995        """
996        Update the output of plotting model 1D
997        """
998        msg = "Plot updating ... "
999        wx.PostEvent(self.parent, StatusEvent(status=msg,type="update"))
1000        self.ready_fit()
1001        #self.calc_thread.ready(0.01)
1002   
1003    def _fill_default_model2D(self, theory,id, qmax,qstep, qmin=None):
1004        """
1005        fill Data2D with default value
1006       
1007        :param theory: Data2D to fill
1008       
1009        """
1010        from DataLoader.data_info import Detector, Source
1011       
1012        detector = Detector()
1013        theory.detector.append(detector)         
1014        theory.source= Source()
1015       
1016        ## Default values   
1017        theory.detector[0].distance= 8000   # mm       
1018        theory.source.wavelength= 6         # A     
1019        theory.detector[0].pixel_size.x= 5  # mm
1020        theory.detector[0].pixel_size.y= 5  # mm
1021       
1022        theory.detector[0].beam_center.x= qmax
1023        theory.detector[0].beam_center.y= qmax
1024   
1025        ## create x_bins and y_bins of the model 2D
1026        pixel_width_x = theory.detector[0].pixel_size.x
1027        pixel_width_y = theory.detector[0].pixel_size.y
1028        center_x      = theory.detector[0].beam_center.x/pixel_width_x
1029        center_y      = theory.detector[0].beam_center.y/pixel_width_y
1030
1031        # theory default: assume the beam
1032        #center is located at the center of sqr detector
1033        xmax = qmax
1034        xmin = -qmax
1035        ymax = qmax
1036        ymin = -qmax
1037       
1038        x=  numpy.linspace(start= -1*qmax,
1039                               stop=qmax,
1040                               num=qstep,
1041                               endpoint=True) 
1042        y = numpy.linspace(start=-1*qmax,
1043                               stop= qmax,
1044                               num= qstep,
1045                               endpoint=True)
1046         
1047        ## use data info instead
1048        new_x = numpy.tile(x, (len(y),1))
1049        new_y = numpy.tile(y, (len(x),1))
1050        new_y = new_y.swapaxes(0,1)
1051       
1052        # all data reuire now in 1d array
1053        qx_data = new_x.flatten()
1054        qy_data = new_y.flatten()
1055       
1056        q_data = numpy.sqrt(qx_data*qx_data+qy_data*qy_data)
1057        # set all True (standing for unmasked) as default
1058        mask    = numpy.ones(len(qx_data), dtype = bool)
1059       
1060        # calculate the range of qx and qy: this way,
1061        # it is a little more independent
1062        x_size = xmax- xmin
1063        y_size = ymax -ymin
1064       
1065        # store x and y bin centers in q space
1066        x_bins  = x
1067        y_bins  = y
1068        # bin size: x- & y-directions
1069        xstep = x_size/len(x_bins-1)
1070        ystep = y_size/len(y_bins-1)
1071       
1072        #theory.data = numpy.zeros(len(mask))
1073        theory.err_data = numpy.ones(len(mask))
1074        theory.qx_data = qx_data
1075        theory.qy_data = qy_data 
1076        theory.q_data = q_data
1077        theory.mask = mask           
1078        theory.x_bins = x_bins 
1079        theory.y_bins = y_bins   
1080       
1081        # max and min taking account of the bin sizes
1082        theory.xmin = xmin
1083        theory.xmax = xmax
1084        theory.ymin = ymin
1085        theory.ymax = ymax
1086        theory.group_id = str(id) + "Model2D"
1087        theory.id = str(id) + "Mode2D"
1088 
1089               
1090    def _complete1D(self, x,y, id, elapsed,index,model,state=None, data=None):
1091        """
1092        Complete plotting 1D data
1093        """ 
1094        try:
1095            new_plot = Data1D(x=x, y=y)
1096            new_plot.symbol = GUIFRAME_ID.CURVE_SYMBOL_NUM
1097            if data != None:
1098                _xaxis, _xunit = data.get_xaxis() 
1099                _yaxis, _yunit = data.get_yaxis() 
1100                new_plot.title = data.name
1101                #if the theory is already plotted use the same group id
1102                #to replot
1103                if id in self.page_finder:
1104                    theory_data = self.page_finder[id].get_theory_data()
1105                    if theory_data is not None:
1106                        group_id = theory_data.group_id[len(theory_data.group_id)-1]
1107                        if group_id not in data.group_id:
1108                            data.group_id.append(group_id)
1109                #data is plotted before the theory, then take its group_id
1110                #assign to the new theory
1111                group_id = data.group_id[len(data.group_id)-1]
1112                if group_id not in new_plot.group_id:
1113                    new_plot.group_id.append(group_id)
1114            else:
1115                _xaxis, _xunit = "\\rm{Q}", 'A^{-1}'
1116                _yaxis, _yunit = "\\rm{Intensity} ", "cm^{-1}"
1117                new_plot.title = "Analytical model 1D "
1118                #find a group id to plot theory without data
1119                group_id =  str(id) + " Model1D" 
1120                if group_id not in new_plot.group_id:
1121                    new_plot.group_id.append(group_id)
1122                new_plot.is_data = False 
1123            new_plot.id =  str(id) + " Model1D" 
1124            #find if this theory was already plotted and replace that plot given
1125            #the same id
1126            if id in self.page_finder:
1127                theory_data = self.page_finder[id].get_theory_data()
1128                if theory_data is not None:
1129                    temp_id = theory_data.id
1130                    new_plot.id = temp_id
1131            new_plot.name = model.name + " ["+ str(model.__class__.__name__)+ "]"
1132            new_plot.xaxis(_xaxis, _xunit)
1133            new_plot.yaxis(_yaxis, _yunit)
1134            self.page_finder[id].set_theory_data(new_plot)
1135            if data is None:
1136                theory_data = self.page_finder[id].get_theory_data()
1137                self.parent.add_data_helper(data_list=[theory_data])
1138                data_id = theory_data.id
1139            else:
1140                data_id = data.id
1141            self.parent.append_theory(data_id=data_id, 
1142                                          theory=new_plot, state=state)
1143            wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot,
1144                                            title= str(new_plot.title)))
1145            current_pg = self.fit_panel.get_page_by_id(id)
1146            wx.PostEvent(current_pg,
1147                Chi2UpdateEvent(output=self._cal_chisqr(data=data,
1148                                                        id=id,
1149                                                        index=index)))
1150            msg = "Plot 1D  complete !"
1151            wx.PostEvent( self.parent, StatusEvent(status=msg, type="stop" ))
1152        except:
1153            raise
1154            #msg = " Error occurred when drawing %s Model 1D: " % new_plot.name
1155            #msg += " %s"  % sys.exc_value
1156            #wx.PostEvent(self.parent, StatusEvent(status=msg, type="stop"))
1157   
1158    def _update2D(self, output,time=None):
1159        """
1160        Update the output of plotting model
1161        """
1162        wx.PostEvent(self.parent, StatusEvent(status="Plot \
1163        #updating ... ", type="update"))
1164        self.ready_fit()
1165        #self.calc_thread.ready(0.01)
1166   
1167    def _complete2D(self, image, data, model, id,  elapsed, index, qmin,
1168                     qmax, state=None,qstep=DEFAULT_NPTS):
1169        """
1170        Complete get the result of modelthread and create model 2D
1171        that can be plot.
1172        """
1173        err_image = numpy.zeros(numpy.shape(image))
1174       
1175        theory= Data2D(image=image, err_image=err_image)
1176        theory.name = model.name
1177       
1178        if data is None:
1179            self._fill_default_model2D(theory=theory, 
1180                                       qmax=qmax, 
1181                                       id=id,
1182                                       qstep=qstep,
1183                                        qmin= qmin)
1184           
1185        else:
1186            theory.id = str(id) + "Model2D"
1187            theory.group_id = str(id) + "Model2D"
1188         
1189            theory.x_bins = data.x_bins
1190            theory.y_bins = data.y_bins
1191            theory.detector = data.detector
1192            theory.source = data.source
1193            theory.is_data = False 
1194            theory.qx_data = data.qx_data
1195            theory.qy_data = data.qy_data
1196            theory.q_data = data.q_data
1197            #numpy.zeros(len(data.err_data))#data.err_data
1198            theory.err_data = err_image
1199            theory.mask = data.mask
1200            ## plot boundaries
1201            theory.ymin = data.ymin
1202            theory.ymax = data.ymax
1203            theory.xmin = data.xmin
1204            theory.xmax = data.xmax
1205        theory.name = model.name + " ["+ str(model.__class__.__name__)+ "]"
1206        theory.title = "Analytical model 2D "
1207        theory_data = deepcopy(theory)
1208        theory_data.name = "Unknown"
1209        self.page_finder[id].set_theory_data(theory_data)
1210       
1211        if data is None:
1212            theory_data = self.page_finder[id].get_theory_data()
1213            self.parent.add_data_helper(data_list=[theory_data])
1214            data_id = theory_data.id
1215        else:
1216            data_id = data.id
1217        self.parent.append_theory(data_id=data_id, 
1218                                          theory=theory, state=state)
1219           
1220        ## plot
1221        wx.PostEvent(self.parent, NewPlotEvent(plot=theory,
1222                         title=theory.title))
1223        # Chisqr in fitpage
1224        current_pg = self.fit_panel.get_page_by_id(id)
1225        wx.PostEvent(current_pg,
1226            Chi2UpdateEvent(output=self._cal_chisqr(data=data, id=id, index=index)))
1227        msg = "Plot 2D complete !"
1228        wx.PostEvent(self.parent, StatusEvent(status=msg, type="stop"))
1229   
1230    def _draw_model2D(self, model, id, data=None, smearer=None,
1231                      description=None, enable2D=False,
1232                      state=None,
1233                      qmin=DEFAULT_QMIN, qmax=DEFAULT_QMAX,
1234                       qstep=DEFAULT_NPTS):
1235        """
1236        draw model in 2D
1237       
1238        :param model: instance of the model to draw
1239        :param description: the description of the model
1240        :param enable2D: when True allows to draw model 2D
1241        :param qmin: the minimum value to  draw model 2D
1242        :param qmax: the maximum value to draw model 2D
1243        :param qstep: the number of division of Qx and Qy of the model to draw
1244           
1245        """
1246        x=  numpy.linspace(start=-1*qmax,
1247                               stop=qmax,
1248                               num=qstep,
1249                               endpoint=True) 
1250        y = numpy.linspace(start= -1*qmax,
1251                               stop=qmax,
1252                               num=qstep,
1253                               endpoint=True)
1254        if model is None:
1255            msg = "Panel with ID: %s does not contained model" % str(id)
1256            raise ValueError, msg
1257        ## use data info instead
1258        if data is not None:
1259            ## check if data2D to plot
1260            if hasattr(data, "x_bins"):
1261                enable2D = True
1262                x = data.x_bins
1263                y = data.y_bins
1264               
1265        if not enable2D:
1266            return None, None
1267        try:
1268            from model_thread import Calc2D
1269            ## If a thread is already started, stop it
1270            if (self.calc_2D is not None) and self.calc_2D.isrunning():
1271                self.calc_2D.stop()
1272
1273            self.calc_2D = Calc2D(x=x,
1274                                    y=y,
1275                                    model=model, 
1276                                    data=data,
1277                                    id=id,
1278                                    smearer=smearer,
1279                                    qmin=qmin,
1280                                    qmax=qmax,
1281                                    qstep=qstep,
1282                                    state=state,
1283                                    completefn=self._complete2D,
1284                                    updatefn=self._update2D)
1285            self.calc_2D.queue()
1286
1287        except:
1288            raise
1289            #msg = " Error occurred when drawing %s Model 2D: " % model.name
1290            #msg += " %s" % sys.exc_value
1291            #wx.PostEvent(self.parent, StatusEvent(status=msg))
1292
1293    def _draw_model1D(self, model, id, data=None, smearer=None,
1294                qmin=DEFAULT_QMIN, qmax=DEFAULT_QMAX, 
1295                state=None,
1296                qstep=DEFAULT_NPTS, enable1D=True):
1297        """
1298        Draw model 1D from loaded data1D
1299       
1300        :param data: loaded data
1301        :param model: the model to plot
1302       
1303        """
1304        x=  numpy.linspace(start=qmin,
1305                           stop=qmax,
1306                           num=qstep,
1307                           endpoint=True
1308                           )
1309        if data is not None:
1310            ## check for data2D
1311            if hasattr(data,"x_bins"):
1312                return
1313            x = data.x
1314            if qmin == DEFAULT_QMIN :
1315                qmin = min(data.x)
1316            if qmax == DEFAULT_QMAX:
1317                qmax = max(data.x) 
1318        if not enable1D:
1319            return 
1320        try:
1321            from model_thread import Calc1D
1322            ## If a thread is already started, stop it
1323            if (self.calc_1D is not None) and self.calc_1D.isrunning():
1324                self.calc_1D.stop()
1325            self.calc_1D = Calc1D(x=x,
1326                                  data=data,
1327                                  model=model,
1328                                  id=id, 
1329                                  qmin=qmin,
1330                                  qmax=qmax,
1331                                  smearer=smearer,
1332                                  state=state,
1333                                  completefn=self._complete1D,
1334                                  updatefn=self._update1D)
1335            self.calc_1D.queue()
1336
1337        except:
1338            msg = " Error occurred when drawing %s Model 1D: " % model.name
1339            msg += " %s" % sys.exc_value
1340            wx.PostEvent(self.parent, StatusEvent(status=msg))
1341
1342    def _cal_chisqr(self, id, data=None, index=None): 
1343        """
1344        Get handy Chisqr using the output from draw1D and 2D,
1345        instead of calling expansive CalcChisqr in guithread
1346        """
1347        # default chisqr
1348        chisqr = None
1349       
1350        # return None if data == None
1351        if data == None: return chisqr
1352       
1353        # Get data: data I, theory I, and data dI in order
1354        if data.__class__.__name__ =="Data2D":
1355            if index == None: 
1356                index = numpy.ones(len(data.data),ntype=bool)
1357            # get rid of zero error points
1358            index = index & (data.err_data != 0) 
1359            fn = data.data[index] 
1360            theory_data = self.page_finder[id].get_theory_data()
1361            gn = theory_data.data[index]
1362            en = data.err_data[index]
1363        else:
1364            # 1 d theory from model_thread is only in the range of index
1365            if index == None:
1366                index = numpy.ones(len(data.y), ntype=bool)
1367            if data.dy == None or data.dy == []:
1368                dy = numpy.ones(len(data.y))
1369            else:
1370                ## Set consitently w/AbstractFitengine:
1371                # But this should be corrected later.
1372                dy = data.dy
1373                dy[dy==0] = 1 
1374            fn = data.y[index] 
1375            theory_data = self.page_finder[id].get_theory_data()
1376            gn = theory_data.y
1377            en = dy[index]
1378        # residual
1379        res = (fn - gn)/en
1380        # get chisqr only w/finite
1381        val = res[numpy.isfinite(res)]*res[numpy.isfinite(res)]
1382        chisqr = numpy.average(val)
1383        return chisqr
1384   
1385   
1386#def profile(fn, *args, **kw):
1387#    import cProfile, pstats, os
1388#    global call_result
1389#    def call():
1390#        global call_result
1391#        call_result = fn(*args, **kw)
1392#    cProfile.runctx('call()', dict(call=call), {}, 'profile.out')
1393#    stats = pstats.Stats('profile.out')
1394#    #stats.sort_stats('time')
1395#    stats.sort_stats('calls')
1396#    stats.print_stats()
1397#    os.unlink('profile.out')
1398#    return call_result
1399if __name__ == "__main__":
1400    i = Plugin()
1401   
1402   
1403   
1404   
Note: See TracBrowser for help on using the repository browser.