source: sasview/sansview/perspectives/fitting/fitting.py @ 2d9c4039

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 2d9c4039 was 6bbeacd4, checked in by Gervaise Alina <gervyh@…>, 14 years ago

remove other type of data

  • Property mode set to 100644
File size: 52.7 KB
Line 
1
2
3################################################################################
4#This software was developed by the University of Tennessee as part of the
5#Distributed Data Analysis of Neutron Scattering Experiments (DANSE)
6#project funded by the US National Science Foundation.
7#
8#See the license text in license.txt
9#
10#copyright 2009, University of Tennessee
11################################################################################
12
13
14import re
15import sys
16import wx
17import logging
18import numpy
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                wx.PostEvent(self.parent, NewPlotEvent(plot=data,
319                                        title=data.title))
320                page = self.fit_panel.set_state(state)   
321            else:
322                wx.PostEvent(self.parent, NewPlotEvent(plot=data,
323                                        title=data.title))
324                #just set data because set_state won't work
325                data = self.parent.create_gui_data(state.data)
326                data.group_id = state.data.group_id
327                self.add_fit_page(data)
328                caption = panel.window_name
329                self.store_data(page=panel.id, data=data, caption=caption)
330                self.mypanels.append(panel) 
331               
332            # get ready for the next set_state
333            self.state_index += 1
334
335            #reset state variables to default when all set_state is finished.
336            if len(self.temp_state) == self.state_index:
337               
338                self.temp_state = []
339                #self.state_index = 0
340                # Make sure the user sees the fitting panel after loading
341                #self.parent.set_perspective(self.perspective)
342                self.on_perspective(event=None)
343        except:
344            self.state_index==0
345            self.temp_state = []
346            raise
347                 
348    def save_fit_state(self, filepath, fitstate): 
349        """
350        save fit page state into file
351        """
352        self.state_reader.write(filename=filepath, fitstate=fitstate)
353       
354    def set_fit_range(self, id, qmin, qmax):
355        """
356        Set the fitting range of a given page
357        """
358        self.page_finder[id].set_range(qmin=qmin, qmax=qmax)
359                   
360    def schedule_for_fit(self,value=0, id=None,fitproblem =None): 
361        """
362        Set the fit problem field to 0 or 1 to schedule that problem to fit.
363        Schedule the specified fitproblem or get the fit problem related to
364        the current page and set value.
365       
366        :param value: integer 0 or 1
367        :param fitproblem: fitproblem to schedule or not to fit
368       
369        """   
370        if fitproblem !=None:
371            fitproblem.schedule_tofit(value)
372        else:
373            self.page_finder[id].schedule_tofit(value)
374         
375    def get_page_finder(self):
376        """
377        return self.page_finder used also by simfitpage.py
378        """ 
379        return self.page_finder
380   
381    def set_page_finder(self,modelname,names,values):
382        """
383        Used by simfitpage.py to reset a parameter given the string constrainst.
384         
385        :param modelname: the name ot the model for with the parameter has to reset
386        :param value: can be a string in this case.
387        :param names: the paramter name
388         
389        :note: expecting park used for fit.
390         
391        """ 
392        sim_page_id = self.sim_page.id
393        for id, value in self.page_finder.iteritems():
394            if id != sim_page_id:
395                list = value.get_model()
396                model = list[0]
397                if model.name == modelname:
398                    value.set_model_param(names, values)
399                    break
400         
401    def split_string(self,item): 
402        """
403        receive a word containing dot and split it. used to split parameterset
404        name into model name and parameter name example: ::
405       
406            paramaterset (item) = M1.A
407            Will return model_name = M1 , parameter name = A
408           
409        """
410        if string.find(item,".")!=-1:
411            param_names= re.split("\.",item)
412            model_name=param_names[0]           
413            ##Assume max len is 3; eg., M0.radius.width
414            if len(param_names) == 3:
415                param_name=param_names[1]+"."+param_names[2]
416            else:
417                param_name=param_names[1]                   
418            return model_name,param_name
419       
420    def stop_fit(self):
421        """
422        Stop the fit engine
423        """
424        if self.calc_fit!= None and self.calc_fit.isrunning():
425            self.calc_fit.stop()
426            wx.PostEvent(self.parent, StatusEvent(status="Fitting  \
427                is cancelled" , type="stop"))
428 
429    def set_smearer(self, id, smearer, qmin=None, qmax=None, draw=True):
430        """
431        Get a smear object and store it to a fit problem
432       
433        :param smearer: smear object to allow smearing data
434       
435        """   
436        if id not in self.page_finder.keys():
437            msg = "Cannot find ID: %s in page_finder" % str(id)
438            raise ValueError, msg
439        self.page_finder[id].set_smearer(smearer)
440        if draw:
441            ## draw model 1D with smeared data
442            data =  self.page_finder[id].get_fit_data()
443            model = self.page_finder[id].get_model()
444            if model is None:
445                return
446            ## if user has already selected a model to plot
447            ## redraw the model with data smeared
448            smear = self.page_finder[id].get_smearer()
449            self.draw_model(model=model, data=data, id=id, smearer=smear,
450                qmin=qmin, qmax=qmax)
451
452    def draw_model(self, model, id, data=None, smearer=None,
453                   enable1D=True, enable2D=False,
454                   qmin=DEFAULT_QMIN, qmax=DEFAULT_QMAX, qstep=DEFAULT_NPTS):
455        """
456        Draw model.
457       
458        :param model: the model to draw
459        :param name: the name of the model to draw
460        :param data: the data on which the model is based to be drawn
461        :param description: model's description
462        :param enable1D: if true enable drawing model 1D
463        :param enable2D: if true enable drawing model 2D
464        :param qmin:  Range's minimum value to draw model
465        :param qmax:  Range's maximum value to draw model
466        :param qstep: number of step to divide the x and y-axis
467             
468        """
469        if data.__class__.__name__ != "Data2D":   
470            ## draw model 1D with no loaded data
471           
472            self._draw_model1D(model=model, 
473                               data=data,
474                               id=id,
475                               enable1D=enable1D, 
476                               smearer=smearer,
477                               qmin=qmin,
478                               qmax=qmax, 
479                               qstep=qstep)
480        else:     
481            ## draw model 2D with no initial data
482             self._draw_model2D(model=model,
483                                id=id,
484                                data=data,
485                                enable2D=enable2D,
486                                smearer=smearer,
487                                qmin=qmin,
488                                qmax=qmax,
489                                qstep=qstep)
490           
491    def onFit(self):
492        """
493        perform fit
494        """
495        ##  count the number of fitproblem schedule to fit
496        fitproblem_count = 0
497        for value in self.page_finder.itervalues():
498            if value.get_scheduled() == 1:
499                fitproblem_count += 1
500               
501        ## if simultaneous fit change automatically the engine to park
502        if fitproblem_count > 1:
503            self._on_change_engine(engine='park')
504           
505        self.fitproblem_count = fitproblem_count 
506         
507        from sans.fit.Fitting import Fit
508        self.fitter = Fit(self._fit_engine)
509       
510        if self._fit_engine == "park":
511            engineType = "Simultaneous Fit"
512        else:
513            engineType = "Single Fit"
514           
515        fproblemId = 0
516        self.current_pg = None
517        for id, value in self.page_finder.iteritems():
518            try:
519                if value.get_scheduled() == 1:
520                    #Get list of parameters name to fit
521                    pars = []
522                    templist = []
523                    page = self.fit_panel.get_page_by_id(id)
524                    templist = page.get_param_list()
525                    for element in templist:
526                        name = str(element[1])
527                        pars.append(name)
528                    #Set Engine  (model , data) related to the page on
529                    self._fit_helper(value=value, pars=pars,
530                                      id=fproblemId, title=engineType) 
531                    fproblemId += 1 
532                    self.current_page_id = page
533            except:
534                raise
535                #msg= "%s error: %s" % (engineType, sys.exc_value)
536                #wx.PostEvent(self.parent, StatusEvent(status=msg, info="error",
537                #                                      type="stop"))
538                #return
539        ## If a thread is already started, stop it
540        #if self.calc_fit!= None and self.calc_fit.isrunning():
541        #    self.calc_fit.stop()
542         #Handler used for park engine displayed message
543        handler = ConsoleUpdate(parent=self.parent,improvement_delta=0.1)
544        ## perform single fit
545        if fitproblem_count == 1:
546            calc_fit = FitThread(parent=self.parent,
547                                    handler = handler,
548                                    fn=self.fitter,
549                                   pars=pars,
550                                   updatefn=handler.update_fit,
551                                   completefn=self._single_fit_completed)
552        else:
553            ## Perform more than 1 fit at the time
554            calc_fit = FitThread(parent=self.parent,
555                                handler=handler,
556                                    fn= self.fitter,
557                                   completefn= self._simul_fit_completed,
558                                  updatefn=handler.update_fit)
559       
560        calc_fit.queue()
561        self.ready_fit(calc_fit=calc_fit)
562     
563    def ready_fit(self, calc_fit):
564        """
565        Ready for another fit
566        """
567        if self.fitproblem_count != None and self.fitproblem_count > 1:
568            calc_fit.ready(2.5)
569           
570        else:
571            time.sleep(0.4)
572           
573    def remove_plot(self, id, theory=False):
574        """
575        remove model plot when a fit page is closed
576        """
577        fitproblem = self.page_finder[id]
578        data = fitproblem.get_fit_data()
579        model = fitproblem.get_model()
580        id = None
581        if model is not None:
582            id = data.id + name
583        if theory:
584            id = data.id
585        group_id = data.group_id[len(data.group_id)-1]
586        wx.PostEvent(self.parent, NewPlotEvent(id=id,
587                                                   group_id=group_id,
588                                                   remove=True))
589           
590    def store_data(self, id, data=None, caption=None):
591        """
592        Helper to save page reference into the plug-in
593       
594        :param page: page to store
595       
596        """
597        #create a fitproblem storing all link to data,model,page creation
598        if not id in self.page_finder.keys():
599            self.page_finder[id] = FitProblem()
600        self.page_finder[id].set_fit_data(data)
601        self.page_finder[id].set_fit_tab_caption(caption)
602       
603    def on_add_new_page(self, event=None):
604        """
605        ask fit panel to create a new empty page
606        """
607        try:
608            page = self.fit_panel.add_empty_page()
609            page_caption = page.window_name
610            # add data associated to the page created
611            if page != None: 
612                self.store_data(id=page.id, caption=page_caption,
613                                data=page.get_data())
614                wx.PostEvent(self.parent, StatusEvent(status="Page Created",
615                                               info="info"))
616            else:
617                msg = "Page was already Created"
618                wx.PostEvent(self.parent, StatusEvent(status=msg,
619                                                       info="warning"))
620        except:
621            raise
622            #msg = "Creating Fit page: %s"%sys.exc_value
623            #wx.PostEvent(self.parent, StatusEvent(status=msg, info="error"))
624       
625       
626    def add_fit_page(self, data):
627        """
628        given a data, ask to the fitting panel to create a new fitting page,
629        get this page and store it into the page_finder of this plug-in
630        """
631        page = self.fit_panel.set_data(data)
632        page_caption = page.window_name
633        #append Data1D to the panel containing its theory
634        #if theory already plotted
635        if page.id in self.page_finder:
636            theory_data = self.page_finder[page.id].get_theory_data()
637            if issubclass(data.__class__, Data2D):
638                data.group_id.append(wx.NewId())
639            else:
640                if theory_data is not None:
641                    group_id = theory_data.group_id[len(theory_data.group_id)-1]
642                    if group_id not in data.group_id:
643                        data.group_id.append(group_id)
644        self.store_data(id=page.id, data=data, caption=page.window_name)
645        if self.sim_page is not None:
646            self.sim_page.draw_page()
647           
648    def _onEVT_SLICER_PANEL(self, event):
649        """
650        receive and event telling to update a panel with a name starting with
651        event.panel_name. this method update slicer panel for a given interactor.
652       
653        :param event: contains type of slicer , paramaters for updating the panel
654            and panel_name to find the slicer 's panel concerned.
655        """
656        for item in self.parent.panels:
657            if self.parent.panels[item].window_caption.startswith(event.panel_name):
658                self.parent.panels[item].set_slicer(event.type, event.params)
659               
660        self.parent._mgr.Update()
661   
662    def _closed_fitpage(self, event):   
663        """
664        request fitpanel to close a given page when its unique data is removed
665        from the plot. close fitpage only when the a loaded data is removed
666        """   
667        if event is None or event.data is None:
668            return
669       
670        if hasattr(event.data,"is_data"):
671            if not event.data.is_data or \
672                event.data.__class__.__name__=="Data1D":
673                self.fit_panel.close_page_with_data(event.data) 
674       
675    def _add_page_onmenu(self, name,fitproblem=None):
676        """
677        Add name of a closed page of fitpanel in a menu
678        """
679        list = self.menu1.GetMenuItems()
680        for item in list:
681            if name == item.GetItemLabel():
682                self.closed_page_dict[name][1] = fitproblem
683               
684        if not name in self.closed_page_dict.keys():   
685            # Post paramters
686            event_id = wx.NewId()
687            self.menu1.Append(event_id, name, "Show %s fit panel" % name)
688            self.closed_page_dict[name]= [event_id, fitproblem]
689            wx.EVT_MENU(self.parent,event_id,  self._open_closed_page)
690       
691    def _open_closed_page(self, event):   
692        """
693        reopen a closed page
694        """
695        for name, value in self.closed_page_dict.iteritems():
696            if event.GetId() in value:
697                id,fitproblem = value
698                if name !="Model":
699                    data= fitproblem.get_fit_data()
700                    page = self.fit_panel.add_fit_page(data= data,reset=True)
701                    if fitproblem != None:
702                        self.page_finder[id] = fitproblem
703                        if self.sim_page != None:
704                            self.sim_page.draw_page()
705                           
706                else:
707                    model = fitproblem
708                    self.fit_panel.add_model_page(model=model, topmenu=True,
709                                                  reset= True)
710                    break
711   
712    def _reset_schedule_problem(self, value=0):
713        """
714        unschedule or schedule all fitproblem to be fit
715        """
716        for fitproblem in self.page_finder.values():
717            fitproblem.schedule_tofit(value)
718           
719    def _fit_helper(self, pars, value, id, title="Single Fit " ):
720        """
721        helper for fitting
722        """
723        metadata = value.get_fit_data()
724        model = value.get_model()
725        smearer = value.get_smearer()
726        qmin, qmax = value.get_range()
727        self.fit_id = id
728        #Create list of parameters for fitting used
729        templist = []
730       
731        try:
732            #Extra list of parameters and their constraints
733            listOfConstraint = []
734           
735            param = value.get_model_param()
736            if len(param) > 0:
737                for item in param:
738                    ## check if constraint
739                    if item[0] != None and item[1] != None:
740                        listOfConstraint.append((item[0],item[1]))
741                   
742            #Do the single fit
743            self.fitter.set_model(model, self.fit_id,
744                                   pars, constraints=listOfConstraint)
745           
746            self.fitter.set_data(data=metadata, id=self.fit_id,
747                                 smearer=smearer, qmin=qmin, qmax=qmax)
748           
749            self.fitter.select_problem_for_fit(id=self.fit_id,
750                                               value=value.get_scheduled())
751            value.clear_model_param()
752        except:
753            raise
754            #msg = title + " error: %s" % sys.exc_value
755            #wx.PostEvent(self.parent, StatusEvent(status=msg, type="stop"))
756         
757    def _onSelect(self,event):
758        """
759        when Select data to fit a new page is created .Its reference is
760        added to self.page_finder
761        """
762        self.panel = event.GetEventObject()
763        Plugin.on_perspective(self, event=event)
764        for plottable in self.panel.graph.plottables:
765            if plottable.__class__.__name__ in ["Data1D", "Theory1D"]:
766                if plottable.name == self.panel.graph.selected_plottable:
767                    data = plottable
768                    self.add_fit_page(data=data)
769                    return
770            else:
771                data = plottable
772                self.add_fit_page(data=data)
773           
774    def _single_fit_completed(self,result, pars, elapsed=None):
775        """
776        Display fit result on one page of the notebook.
777       
778        :param result: result of fit
779        :param pars: list of names of parameters fitted
780        :param current_pg: the page where information will be displayed
781        :param qmin: the minimum value of x to replot the model
782        :param qmax: the maximum value of x to replot model
783         
784        """     
785        try:
786            if result ==None:
787                msg= "Simple Fitting Stop !!!"
788                wx.PostEvent(self.parent, StatusEvent(status=msg,info="warning",
789                                                      type="stop"))
790                return
791            if not numpy.isfinite(result.fitness) or \
792                    numpy.any(result.pvec == None) or \
793                    not numpy.all(numpy.isfinite(result.pvec)):
794                msg = "Single Fitting did not converge!!!"
795                wx.PostEvent(self.parent, 
796                             StatusEvent(status=msg, type="stop"))
797                return
798            page_id = None
799            for id, value in self.page_finder.iteritems():   
800                if value.get_scheduled()==1:
801                    model = value.get_model()
802                    page_id = id
803                    break
804            param_name = []
805            i = 0
806            for name in pars:
807                param_name.append(name)
808               
809            cpage = self.fit_panel.get_page_by_id(page_id)
810            cpage.onsetValues(result.fitness, 
811                              param_name, result.pvec,result.stderr)
812           
813        except:
814            msg = "Single Fit completed but Following"
815            msg += " error occurred:%s" % sys.exc_value
816            wx.PostEvent(self.parent, StatusEvent(status=msg, info="error",
817                                                  type="stop"))
818            return
819       
820    def _simul_fit_completed(self, result, pars=None, elapsed=None):
821        """
822        Parameter estimation completed,
823        display the results to the user
824       
825        :param alpha: estimated best alpha
826        :param elapsed: computation time
827       
828        """
829        ## fit more than 1 model at the same time
830        try:
831            msg = "" 
832            if result ==None:
833                msg= "Complex Fitting Stop !!!"
834                wx.PostEvent(self.parent, StatusEvent(status=msg,type="stop"))
835                return
836            if not numpy.isfinite(result.fitness) or numpy.any(result.pvec ==None )or not numpy.all(numpy.isfinite(result.pvec) ):
837                msg= "Simultaneous Fitting did not converge!!!"
838                wx.PostEvent(self.parent, StatusEvent(status=msg,type="stop"))
839                return
840             
841            for id, value in self.page_finder.iteritems():   
842                if value.get_scheduled() == 1:
843                    model = value.get_model()
844                    data =  value.get_fit_data()
845                    small_param_name = []
846                    small_out = []
847                    small_cov = []
848                    i = 0
849                    #Separate result in to data corresponding to each page
850                    for p in result.parameters:
851                        model_name,param_name = self.split_string(p.name) 
852                        if model.name == model_name:
853                            p_name= model.name+"."+param_name
854                            if p.name == p_name:     
855                                if p.value != None and numpy.isfinite(p.value):
856                                    small_out.append(p.value )
857                                    small_param_name.append(param_name)
858                                    small_cov.append(p.stderr)
859
860                    # Display result on each page
861                    cpage = self.fit_panel.get_page_by_id(id)
862                    cpage.onsetValues(result.fitness,
863                                      small_param_name,
864                                      small_out,small_cov)
865        except:
866            raise
867            #msg= "Simultaneous Fit completed"
868            #msg +=" but Following error occurred:%s"%sys.exc_value
869            #wx.PostEvent(self.parent, StatusEvent(status=msg,type="stop"))
870            #return
871             
872    def _on_show_panel(self, event):
873        """
874        """
875        pass
876       
877    def _onset_engine_park(self,event):
878        """
879        set engine to park
880        """
881        self._on_change_engine('park')
882       
883    def _onset_engine_scipy(self,event):
884        """
885        set engine to scipy
886        """
887        self._on_change_engine('scipy')
888       
889    def _on_slicer_event(self, event):
890        """
891        Receive a panel as event and send it to guiframe
892       
893        :param event: event containing a panel
894       
895        """
896        if event.panel is not None:
897            new_panel = event.panel
898            self.slicer_panels.append(event.panel)
899            # Set group ID if available
900            event_id = self.parent.popup_panel(new_panel)
901            #self.menu3.Append(event_id, new_panel.window_caption,
902            #                 "Show %s plot panel" % new_panel.window_caption)
903            # Set id to allow us to reference the panel later
904         
905            new_panel.uid = event_id
906            self.mypanels.append(new_panel) 
907       
908    def _onclearslicer(self, event):
909        """
910        Clear the boxslicer when close the panel associate with this slicer
911        """
912        name =event.GetPane().caption
913   
914        for panel in self.slicer_panels:
915            if panel.window_caption==name:
916               
917                for item in self.parent.panels:
918                    if hasattr(self.parent.panels[item],"uid"):
919                        if self.parent.panels[item].uid ==panel.base.uid:
920                            self.parent.panels[item].onClearSlicer(event)
921                            self.parent._mgr.Update()
922                            break 
923                break
924   
925    def _return_engine_type(self):
926        """
927        return the current type of engine
928        """
929        return self._fit_engine
930     
931     
932    def _on_change_engine(self, engine='park'):
933        """
934        Allow to select the type of engine to perform fit
935       
936        :param engine: the key work of the engine
937       
938        """
939        ## saving fit engine name
940        self._fit_engine = engine
941        ## change menu item state
942        if engine=="park":
943            self.menu1.FindItemByPosition(0).Check(False)
944            self.menu1.FindItemByPosition(1).Check(True)
945        else:
946            self.menu1.FindItemByPosition(0).Check(True)
947            self.menu1.FindItemByPosition(1).Check(False)
948        ## post a message to status bar
949        msg = "Engine set to: %s" % self._fit_engine
950        wx.PostEvent(self.parent, 
951                     StatusEvent(status=msg))
952        ## Bind every open fit page with a newevent to
953        #know the current fitting engine
954        #import fitpage
955        event = fitpage.FitterTypeEvent()
956        event.type = self._fit_engine
957        wx.PostEvent(self.fit_panel, event)
958       
959    def _on_model_panel(self, evt):
960        """
961        react to model selection on any combo box or model menu.plot the model 
962       
963        :param evt: wx.combobox event
964       
965        """
966        model = evt.model
967        id = evt.id
968        qmin = evt.qmin
969        qmax = evt.qmax
970        smearer = evt.smearer
971        if model == None:
972            return
973       
974        if self.page_finder[id].get_model() is None:
975            model.name = "M" + str(self.index_model)
976            self.index_model += 1 
977        else:
978            model.name = self.page_finder[id].get_model().name
979        # save the name containing the data name with the appropriate model
980        self.page_finder[id].set_model(model)
981        self.page_finder[id].set_range(qmin=qmin, qmax=qmax)
982        if self.sim_page is not None:
983            self.sim_page.draw_page()
984       
985    def _update1D(self,x, output):
986        """
987        Update the output of plotting model 1D
988        """
989        msg = "Plot updating ... "
990        wx.PostEvent(self.parent, StatusEvent(status=msg,type="update"))
991        self.ready_fit()
992        #self.calc_thread.ready(0.01)
993   
994    def _fill_default_model2D(self, theory,id, qmax,qstep, qmin=None):
995        """
996        fill Data2D with default value
997       
998        :param theory: Data2D to fill
999       
1000        """
1001        from DataLoader.data_info import Detector, Source
1002       
1003        detector = Detector()
1004        theory.detector.append(detector)         
1005        theory.source= Source()
1006       
1007        ## Default values   
1008        theory.detector[0].distance= 8000   # mm       
1009        theory.source.wavelength= 6         # A     
1010        theory.detector[0].pixel_size.x= 5  # mm
1011        theory.detector[0].pixel_size.y= 5  # mm
1012       
1013        theory.detector[0].beam_center.x= qmax
1014        theory.detector[0].beam_center.y= qmax
1015   
1016        ## create x_bins and y_bins of the model 2D
1017        pixel_width_x = theory.detector[0].pixel_size.x
1018        pixel_width_y = theory.detector[0].pixel_size.y
1019        center_x      = theory.detector[0].beam_center.x/pixel_width_x
1020        center_y      = theory.detector[0].beam_center.y/pixel_width_y
1021
1022        # theory default: assume the beam
1023        #center is located at the center of sqr detector
1024        xmax = qmax
1025        xmin = -qmax
1026        ymax = qmax
1027        ymin = -qmax
1028       
1029        x=  numpy.linspace(start= -1*qmax,
1030                               stop=qmax,
1031                               num=qstep,
1032                               endpoint=True) 
1033        y = numpy.linspace(start=-1*qmax,
1034                               stop= qmax,
1035                               num= qstep,
1036                               endpoint=True)
1037         
1038        ## use data info instead
1039        new_x = numpy.tile(x, (len(y),1))
1040        new_y = numpy.tile(y, (len(x),1))
1041        new_y = new_y.swapaxes(0,1)
1042       
1043        # all data reuire now in 1d array
1044        qx_data = new_x.flatten()
1045        qy_data = new_y.flatten()
1046       
1047        q_data = numpy.sqrt(qx_data*qx_data+qy_data*qy_data)
1048        # set all True (standing for unmasked) as default
1049        mask    = numpy.ones(len(qx_data), dtype = bool)
1050       
1051        # calculate the range of qx and qy: this way,
1052        # it is a little more independent
1053        x_size = xmax- xmin
1054        y_size = ymax -ymin
1055       
1056        # store x and y bin centers in q space
1057        x_bins  = x
1058        y_bins  = y
1059        # bin size: x- & y-directions
1060        xstep = x_size/len(x_bins-1)
1061        ystep = y_size/len(y_bins-1)
1062       
1063        #theory.data = numpy.zeros(len(mask))
1064        theory.err_data = numpy.ones(len(mask))
1065        theory.qx_data = qx_data
1066        theory.qy_data = qy_data 
1067        theory.q_data = q_data
1068        theory.mask = mask           
1069        theory.x_bins = x_bins 
1070        theory.y_bins = y_bins   
1071       
1072        # max and min taking account of the bin sizes
1073        theory.xmin = xmin
1074        theory.xmax = xmax
1075        theory.ymin = ymin
1076        theory.ymax = ymax
1077        theory.group_id = str(id) + "Model2D"
1078        theory.id = str(id) + "Mode2D"
1079 
1080               
1081    def _complete1D(self, x,y, id, elapsed,index,model,data=None):
1082        """
1083        Complete plotting 1D data
1084        """ 
1085        try:
1086            new_plot = Data1D(x=x, y=y)
1087            new_plot.symbol = GUIFRAME_ID.CURVE_SYMBOL_NUM
1088            if data != None:
1089                _xaxis, _xunit = data.get_xaxis() 
1090                _yaxis, _yunit = data.get_yaxis() 
1091                new_plot.title = data.name
1092                #if the theory is already plotted use the same group id
1093                #to replot
1094                if id in self.page_finder:
1095                    theory_data = self.page_finder[id].get_theory_data()
1096                    if theory_data is not None:
1097                        print "theory_data", theory_data.group_id
1098                        group_id = theory_data.group_id[len(theory_data.group_id)-1]
1099                        if group_id not in data.group_id:
1100                            data.group_id.append(group_id)
1101                        print "data", data.group_id
1102                #data is plotted before the theory, then take its group_id
1103                #assign to the new theory
1104                group_id = data.group_id[len(data.group_id)-1]
1105                if group_id not in new_plot.group_id:
1106                    new_plot.group_id.append(group_id)
1107            else:
1108                _xaxis, _xunit = "\\rm{Q}", 'A^{-1}'
1109                _yaxis, _yunit = "\\rm{Intensity} ", "cm^{-1}"
1110                new_plot.title = "Analytical model 1D "
1111                #find a group id to plot theory without data
1112                group_id =  str(id) + " Model1D" 
1113                if group_id not in new_plot.group_id:
1114                    new_plot.group_id.append(group_id)
1115                new_plot.is_data = False 
1116            new_plot.id =  str(id) + " Model1D" 
1117            #find if this theory was already plotted and replace that plot given
1118            #the same id
1119            if id in self.page_finder:
1120                theory_data = self.page_finder[id].get_theory_data()
1121                if theory_data is not None:
1122                    temp_id = theory_data.id
1123                    new_plot.id = temp_id
1124            new_plot.name = model.name + " ["+ str(model.__class__.__name__)+ "]"
1125            new_plot.xaxis(_xaxis, _xunit)
1126            new_plot.yaxis(_yaxis, _yunit)
1127            self.page_finder[id].set_theory_data(new_plot)
1128           
1129            if data is None:
1130                wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot,
1131                             title=str(new_plot.title)))
1132            else:
1133                wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot,
1134                                            title= str(new_plot.title)))
1135            current_pg = self.fit_panel.get_page_by_id(id)
1136            wx.PostEvent(current_pg,
1137                Chi2UpdateEvent(output=self._cal_chisqr(data=data,
1138                                                        id=id,
1139                                                        index=index)))
1140            msg = "Plot 1D  complete !"
1141            wx.PostEvent( self.parent, StatusEvent(status=msg, type="stop" ))
1142        except:
1143            raise
1144            #msg = " Error occurred when drawing %s Model 1D: " % new_plot.name
1145            #msg += " %s"  % sys.exc_value
1146            #wx.PostEvent(self.parent, StatusEvent(status=msg, type="stop"))
1147   
1148    def _update2D(self, output,time=None):
1149        """
1150        Update the output of plotting model
1151        """
1152        wx.PostEvent(self.parent, StatusEvent(status="Plot \
1153        #updating ... ", type="update"))
1154        self.ready_fit()
1155        #self.calc_thread.ready(0.01)
1156   
1157    def _complete2D(self, image, data, model, id,  elapsed, index, qmin,
1158                     qmax, qstep=DEFAULT_NPTS):
1159        """
1160        Complete get the result of modelthread and create model 2D
1161        that can be plot.
1162        """
1163        err_image = numpy.zeros(numpy.shape(image))
1164       
1165        theory= Data2D(image=image, err_image=err_image)
1166        theory.name = model.name
1167       
1168        if data is None:
1169            self._fill_default_model2D(theory=theory, 
1170                                       qmax=qmax, 
1171                                       id=id,
1172                                       qstep=qstep,
1173                                        qmin= qmin)
1174           
1175        else:
1176            theory.id = str(id) + "Model2D"
1177            theory.group_id = str(id) + "Model2D"
1178         
1179            theory.x_bins = data.x_bins
1180            theory.y_bins = data.y_bins
1181            theory.detector = data.detector
1182            theory.source = data.source
1183            theory.is_data = False 
1184            theory.qx_data = data.qx_data
1185            theory.qy_data = data.qy_data
1186            theory.q_data = data.q_data
1187            #numpy.zeros(len(data.err_data))#data.err_data
1188            theory.err_data = err_image
1189            theory.mask = data.mask
1190            ## plot boundaries
1191            theory.ymin = data.ymin
1192            theory.ymax = data.ymax
1193            theory.xmin = data.xmin
1194            theory.xmax = data.xmax
1195        theory.title = "Analytical model 2D "
1196        self.page_finder[id].set_theory_data(theory)
1197       
1198        ## plot
1199        wx.PostEvent(self.parent, NewPlotEvent(plot=theory,
1200                         title=theory.title))
1201        # Chisqr in fitpage
1202        current_pg = self.fit_panel.get_page_by_id(id)
1203        wx.PostEvent(current_pg,
1204            Chi2UpdateEvent(output=self._cal_chisqr(data=data, id=id, index=index)))
1205        msg = "Plot 2D complete !"
1206        wx.PostEvent(self.parent, StatusEvent(status=msg, type="stop"))
1207   
1208    def _draw_model2D(self, model, id, data=None, smearer=None,
1209                      description=None, enable2D=False,
1210                      qmin=DEFAULT_QMIN, qmax=DEFAULT_QMAX,
1211                       qstep=DEFAULT_NPTS):
1212        """
1213        draw model in 2D
1214       
1215        :param model: instance of the model to draw
1216        :param description: the description of the model
1217        :param enable2D: when True allows to draw model 2D
1218        :param qmin: the minimum value to  draw model 2D
1219        :param qmax: the maximum value to draw model 2D
1220        :param qstep: the number of division of Qx and Qy of the model to draw
1221           
1222        """
1223        x=  numpy.linspace(start=-1*qmax,
1224                               stop=qmax,
1225                               num=qstep,
1226                               endpoint=True) 
1227        y = numpy.linspace(start= -1*qmax,
1228                               stop=qmax,
1229                               num=qstep,
1230                               endpoint=True)
1231        if model is None:
1232            msg = "Panel with ID: %s does not contained model" % str(id)
1233            raise ValueError, msg
1234        ## use data info instead
1235        if data is not None:
1236            ## check if data2D to plot
1237            if hasattr(data, "x_bins"):
1238                enable2D = True
1239                x = data.x_bins
1240                y = data.y_bins
1241               
1242        if not enable2D:
1243            return None, None
1244        try:
1245            from model_thread import Calc2D
1246            ## If a thread is already started, stop it
1247            if (self.calc_2D is not None) and self.calc_2D.isrunning():
1248                self.calc_2D.stop()
1249
1250            self.calc_2D = Calc2D(x=x,
1251                                    y=y,
1252                                    model=model, 
1253                                    data=data,
1254                                    id=id,
1255                                    smearer=smearer,
1256                                    qmin=qmin,
1257                                    qmax=qmax,
1258                                    qstep=qstep,
1259                                    completefn=self._complete2D,
1260                                    updatefn=self._update2D)
1261            self.calc_2D.queue()
1262
1263        except:
1264            raise
1265            #msg = " Error occurred when drawing %s Model 2D: " % model.name
1266            #msg += " %s" % sys.exc_value
1267            #wx.PostEvent(self.parent, StatusEvent(status=msg))
1268
1269    def _draw_model1D(self, model, id, data=None, smearer=None,
1270                qmin=DEFAULT_QMIN, qmax=DEFAULT_QMAX, 
1271                qstep=DEFAULT_NPTS, enable1D=True):
1272        """
1273        Draw model 1D from loaded data1D
1274       
1275        :param data: loaded data
1276        :param model: the model to plot
1277       
1278        """
1279        x=  numpy.linspace(start=qmin,
1280                           stop=qmax,
1281                           num=qstep,
1282                           endpoint=True
1283                           )
1284        if data is not None:
1285            ## check for data2D
1286            if hasattr(data,"x_bins"):
1287                return
1288            x = data.x
1289            if qmin == DEFAULT_QMIN :
1290                qmin = min(data.x)
1291            if qmax == DEFAULT_QMAX:
1292                qmax = max(data.x) 
1293        if not enable1D:
1294            return 
1295        try:
1296            from model_thread import Calc1D
1297            ## If a thread is already started, stop it
1298            if (self.calc_1D is not None) and self.calc_1D.isrunning():
1299                self.calc_1D.stop()
1300            self.calc_1D = Calc1D(x=x,
1301                                  data=data,
1302                                  model=model,
1303                                  id=id, 
1304                                  qmin=qmin,
1305                                  qmax=qmax,
1306                                  smearer=smearer,
1307                                  completefn=self._complete1D,
1308                                  updatefn=self._update1D)
1309            self.calc_1D.queue()
1310
1311        except:
1312            msg = " Error occurred when drawing %s Model 1D: " % model.name
1313            msg += " %s" % sys.exc_value
1314            wx.PostEvent(self.parent, StatusEvent(status=msg))
1315
1316    def _cal_chisqr(self, id, data=None, index=None): 
1317        """
1318        Get handy Chisqr using the output from draw1D and 2D,
1319        instead of calling expansive CalcChisqr in guithread
1320        """
1321        # default chisqr
1322        chisqr = None
1323       
1324        # return None if data == None
1325        if data == None: return chisqr
1326       
1327        # Get data: data I, theory I, and data dI in order
1328        if data.__class__.__name__ =="Data2D":
1329            if index == None: 
1330                index = numpy.ones(len(data.data),ntype=bool)
1331            # get rid of zero error points
1332            index = index & (data.err_data != 0) 
1333            fn = data.data[index] 
1334            theory_data = self.page_finder[id].get_theory_data()
1335            gn = theory_data.data[index]
1336            en = data.err_data[index]
1337        else:
1338            # 1 d theory from model_thread is only in the range of index
1339            if index == None:
1340                index = numpy.ones(len(data.y), ntype=bool)
1341            if data.dy == None or data.dy == []:
1342                dy = numpy.ones(len(data.y))
1343            else:
1344                ## Set consitently w/AbstractFitengine:
1345                # But this should be corrected later.
1346                dy = data.dy
1347                dy[dy==0] = 1 
1348            fn = data.y[index] 
1349            theory_data = self.page_finder[id].get_theory_data()
1350            gn = theory_data.y
1351            en = dy[index]
1352        # residual
1353        res = (fn - gn)/en
1354        # get chisqr only w/finite
1355        val = res[numpy.isfinite(res)]*res[numpy.isfinite(res)]
1356        chisqr = numpy.average(val)
1357        return chisqr
1358   
1359   
1360#def profile(fn, *args, **kw):
1361#    import cProfile, pstats, os
1362#    global call_result
1363#    def call():
1364#        global call_result
1365#        call_result = fn(*args, **kw)
1366#    cProfile.runctx('call()', dict(call=call), {}, 'profile.out')
1367#    stats = pstats.Stats('profile.out')
1368#    #stats.sort_stats('time')
1369#    stats.sort_stats('calls')
1370#    stats.print_stats()
1371#    os.unlink('profile.out')
1372#    return call_result
1373if __name__ == "__main__":
1374    i = Plugin()
1375   
1376   
1377   
1378   
Note: See TracBrowser for help on using the repository browser.