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

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 bb18ef1 was bb18ef1, checked in by Gervaise Alina <gervyh@…>, 15 years ago

some changes of sansview review

  • Property mode set to 100644
File size: 47.3 KB
Line 
1import  re
2import sys, wx, logging
3import string, numpy, math
4
5from copy import deepcopy
6from danse.common.plottools.plottables import Data1D, Theory1D,Data2D
7from danse.common.plottools.PlotPanel import PlotPanel
8from sans.guicomm.events import NewPlotEvent, StatusEvent 
9from sans.guicomm.events import EVT_SLICER_PANEL,ERR_DATA
10
11from sans.fit.AbstractFitEngine import Model,FitData1D,FitData2D#,Data,
12from fitproblem import FitProblem
13from fitpanel import FitPanel
14from fit_thread import FitThread
15import models
16import fitpage1D
17
18DEFAULT_BEAM = 0.005
19DEFAULT_QMIN = 0.0
20DEFAULT_QMAX = 0.1
21DEFAULT_NPTS = 50
22import time
23import thread
24
25class Plugin:
26    """
27        Fitting plugin is used to perform fit
28    """
29    def __init__(self):
30        ## Plug-in name
31        self.sub_menu = "Fitting"
32       
33        ## Reference to the parent window
34        self.parent = None
35        #Provide list of models existing in the application
36        self.menu_mng = models.ModelManager()
37        ## List of panels for the simulation perspective (names)
38        self.perspective = []
39        #list of panel to send to guiframe
40        self.mypanels=[]
41        # reference to the current running thread
42        self.calc_thread = None
43        # Start with a good default
44        self.elapsed = 0.022
45        # the type of optimizer selected, park or scipy
46        self.fitter  = None
47        #Flag to let the plug-in know that it is running stand alone
48        self.standalone=True
49        ## Fit engine
50        self._fit_engine = 'scipy'
51        #List of selected data
52        self.selected_data_list=[]
53        # Log startup
54        logging.info("Fitting plug-in started")   
55        # model 2D view
56        self.model2D_id=None
57        #keep reference of the simultaneous fit page
58        self.sim_page=None
59        #dictionary containing data name and error on dy of that data
60        self.err_dy={}
61       
62    def _on_data_error(self, event):
63        """
64            receives and event from plotting plu-gins to store the data name and
65            their errors of y coordinates for 1Data hide and show error
66        """
67        self.err_dy= event.err_dy
68       
69    def populate_menu(self, id, owner):
70        """
71            Create a menu for the Fitting plug-in
72            @param id: id to create a menu
73            @param owner: owner of menu
74            @ return : list of information to populate the main menu
75        """
76        #Menu for fitting
77        self.menu1 = wx.Menu()
78        id1 = wx.NewId()
79        self.menu1.Append(id1, '&Simultaneous')
80        wx.EVT_MENU(owner, id1, self.on_add_sim_page)
81        #Set park engine
82        id3 = wx.NewId()
83        scipy_help= "Scipy Engine: Perform Simple fit. More in Help window...."
84        self.menu1.Append(id3, "Scipy",scipy_help) 
85        wx.EVT_MENU(owner, id3,  self._onset_engine_scipy)
86       
87        id3 = wx.NewId()
88        park_help = "Park Engine: Perform Complex fit. More in Help window...."
89        self.menu1.Append(id3, "park",park_help) 
90        wx.EVT_MENU(owner, id3,  self._onset_engine_park)
91       
92        #menu for model
93        menu2 = wx.Menu()
94   
95        self.menu_mng.populate_menu(menu2, owner)
96        id2 = wx.NewId()
97        owner.Bind(models.EVT_MODEL,self._on_model_menu)
98     
99        self.fit_panel.set_owner(owner)
100        self.fit_panel.set_model_list(self.menu_mng.get_model_list())
101        owner.Bind(fitpage1D.EVT_MODEL_BOX,self._on_model_panel)
102       
103        #create  menubar items
104        return [(id, self.menu1, "Fitting"),(id2, menu2, "Model")]
105   
106    def on_add_sim_page(self, event):
107        """
108            Create a page to access simultaneous fit option
109        """
110        self.sim_page= self.fit_panel.add_sim_page()
111        self.sim_page.add_model(self.page_finder)
112       
113       
114    def help(self, evt):
115        """
116            Show a general help dialog.
117            TODO: replace the text with a nice image
118        """
119        from helpPanel import  HelpWindow
120        frame = HelpWindow(None, -1, 'HelpWindow')   
121        frame.Show(True)
122       
123       
124    def get_context_menu(self, graph=None):
125        """
126            Get the context menu items available for P(r)
127            @param graph: the Graph object to which we attach the context menu
128            @return: a list of menu items with call-back function
129        """
130        self.graph=graph
131        for item in graph.plottables:
132            if item.__class__.__name__ is "Data2D":
133                return [["Select data  for Fitting",\
134                          "Dialog with fitting parameters ", self._onSelect]] 
135            else:
136               
137                if item.name==graph.selected_plottable :
138                    if not hasattr(item, "group_id"):
139                        return []
140                    return [["Select data  for Fitting", \
141                             "Dialog with fitting parameters ", self._onSelect]] 
142        return []   
143
144
145    def get_panels(self, parent):
146        """
147            Create and return a list of panel objects
148        """
149        self.parent = parent
150        # Creation of the fit panel
151        self.fit_panel = FitPanel(self.parent, -1)
152        #Set the manager for the main panel
153        self.fit_panel.set_manager(self)
154        # List of windows used for the perspective
155        self.perspective = []
156        self.perspective.append(self.fit_panel.window_name)
157        # take care of saving  data, model and page associated with each other
158        self.page_finder = {}
159        #index number to create random model name
160        self.index_model = 0
161        self.index_theory= 0
162        self.parent.Bind(EVT_SLICER_PANEL, self._on_slicer_event)
163        self.parent.Bind( ERR_DATA, self._on_data_error)
164       
165        #Send the fitting panel to guiframe
166        self.mypanels.append(self.fit_panel)
167        return self.mypanels
168
169       
170    def _on_slicer_event(self, event):
171        """
172            Receive a panel as event and send it to guiframe
173            @param event: event containing a panel
174        """
175        if event.panel!=None:
176            new_panel = event.panel
177            # Set group ID if available
178            event_id = self.parent.popup_panel(new_panel)
179            self.menu1.Append(event_id, new_panel.window_caption, 
180                             "Show %s plot panel" % new_panel.window_caption)
181            # Set UID to allow us to reference the panel later
182            new_panel.uid = event_id
183            new_panel
184            self.mypanels.append(new_panel) 
185        return   
186   
187       
188    def _on_show_panel(self, event):
189        print "_on_show_panel: fitting"
190     
191     
192    def get_perspective(self):
193        """
194            Get the list of panel names for this perspective
195        """
196        return self.perspective
197   
198   
199    def on_perspective(self, event):
200        """
201            Call back function for the perspective menu item.
202            We notify the parent window that the perspective
203            has changed.
204        """
205        self.parent.set_perspective(self.perspective)
206   
207   
208    def post_init(self):
209        """
210            Post initialization call back to close the loose ends
211            [Somehow openGL needs this call]
212        """
213        self.parent.set_perspective(self.perspective)
214
215
216    def copy_data(self, item, dy):
217        """
218            receive a data 1D and the list of errors on dy
219            and create a new data1D data
220            @param return
221        """
222        detector=None
223        source=None
224        dxl=None
225        dxw=None
226        if hasattr(item, "dxl"):
227            dxl = item.dxl
228        if hasattr(item, "dxw"):
229            dxw = item.dxw
230        if hasattr(item, "detector"):
231            detector =item.detector
232        if hasattr(item, "source"):
233            source =item.source
234        from sans.guiframe import dataFitting
235        data= dataFitting.Data1D(x=item.x, y=item.y, dy=dy, dxl=dxl, dxw=dxw)
236        data.name=item.name
237        data.detector=detector
238        data.source= source
239        return data
240
241
242    def _onSelect(self,event):
243        """
244            when Select data to fit a new page is created .Its reference is
245            added to self.page_finder
246        """
247        self.panel = event.GetEventObject()
248        for item in self.panel.graph.plottables:
249            if item.name == self.panel.graph.selected_plottable:
250                if len(self.err_dy)>0:
251                    if item.name in  self.err_dy.iterkeys():
252                        dy= self.err_dy[item.name]
253                        data= self.copy_data(item, dy)
254                    else:
255                        data= item
256                else:
257                    if item.dy==None:
258                        dy= numpy.zeros(len(item.y))
259                        dy[dy==0]=1
260                        data= self.copy_data(item, dy)
261                    else:
262                        data= item
263            else:
264                data= item
265            ## create anew page                   
266            if item.name == self.panel.graph.selected_plottable or\
267                 item.__class__.__name__ is "Data2D":
268                try:
269                    page = self.fit_panel.add_fit_page(data)
270                    # add data associated to the page created
271                    if page !=None:   
272                        #create a fitproblem storing all link to data,model,page creation
273                        self.page_finder[page]= FitProblem()
274                        ## item is almost the same as data but contains
275                        ## axis info for plotting
276                        self.page_finder[page].add_plotted_data(item)
277                        self.page_finder[page].add_fit_data(data)
278                       
279                        wx.PostEvent(self.parent, StatusEvent(status="Page Created"))
280                    else:
281                        wx.PostEvent(self.parent, StatusEvent(status="Page was already Created"))
282                except:
283                    wx.PostEvent(self.parent, StatusEvent(status="Creating Fit page: %s"\
284                    %sys.exc_value))
285                    return
286                   
287                   
288    def schedule_for_fit(self,value=0,fitproblem =None): 
289        """
290            Set the fit problem field to 0 or 1 to schedule that problem to fit.
291            Schedule the specified fitproblem or get the fit problem related to
292            the current page and set value.
293            @param value : integer 0 or 1
294            @param fitproblem: fitproblem to schedule or not to fit
295        """   
296        if fitproblem !=None:
297            fitproblem.schedule_tofit(value)
298        else:
299            current_pg=self.fit_panel.get_current_page() 
300            for page, val in self.page_finder.iteritems():
301                if page ==current_pg :
302                    val.schedule_tofit(value)
303                    break
304                     
305                   
306    def get_page_finder(self):
307        """ @return self.page_finder used also by simfitpage.py""" 
308        return self.page_finder
309   
310   
311    def set_page_finder(self,modelname,names,values):
312        """
313             Used by simfitpage.py to reset a parameter given the string constrainst.
314             @param modelname: the name ot the model for with the parameter has to reset
315             @param value: can be a string in this case.
316             @param names: the paramter name
317             @note: expecting park used for fit.
318        """ 
319        sim_page= self.sim_page
320        for page, value in self.page_finder.iteritems():
321            if page != sim_page:
322                list=value.get_model()
323                model = list[0]
324                if model.name== modelname:
325                    value.set_model_param(names,values)
326                    break
327
328   
329                           
330    def split_string(self,item): 
331        """
332            receive a word containing dot and split it. used to split parameterset
333            name into model name and parameter name example:
334            paramaterset (item) = M1.A
335            @return model_name =M1 , parameter name =A
336        """
337        if string.find(item,".")!=-1:
338            param_names= re.split("\.",item)
339            model_name=param_names[0]
340            param_name=param_names[1] 
341            return model_name,param_name
342       
343   
344    def _single_fit_completed(self,result,pars,cpage,qmin,qmax,elapsed=None,
345                              ymin=None, ymax=None, xmin=None, xmax=None):
346        """
347            Display fit result on one page of the notebook.
348            @param result: result of fit
349            @param pars: list of names of parameters fitted
350            @param current_pg: the page where information will be displayed
351            @param qmin: the minimum value of x to replot the model
352            @param qmax: the maximum value of x to replot model
353         
354        """
355        wx.PostEvent(self.parent, StatusEvent(status="Single fit \
356        complete! " , type="stop"))
357        try:
358            for page, value in self.page_finder.iteritems():
359                if page==cpage :
360                    list = value.get_model()
361                    model= list[0]
362                    break
363            i = 0
364            for name in pars:
365                if result.pvec.__class__==numpy.float64:
366                    model.setParam(name,result.pvec)
367                else:
368                    model.setParam(name,result.pvec[i])
369                    i += 1
370            ## Reset values of the current page to fit result
371            cpage.onsetValues(result.fitness, result.pvec,result.stderr)
372            ## plot the current model with new param
373            self.plot_helper(currpage=cpage,qmin=qmin,qmax=qmax,
374                             ymin=ymin, ymax=ymax,
375                             xmin=xmin, xmax=xmax,title=None)
376        except:
377            msg= "Single Fit completed but Following error occurred:"
378            wx.PostEvent(self.parent, StatusEvent(status="%s %s" % (msg, sys.exc_value)))
379            return
380       
381       
382    def _simul_fit_completed(self,result,qmin,qmax, elapsed,pars=None,cpage=None,
383                             xmin=None, xmax=None, ymin=None, ymax=None):
384        """
385            Parameter estimation completed,
386            display the results to the user
387            @param alpha: estimated best alpha
388            @param elapsed: computation time
389        """
390        if cpage!=None:
391            self._single_fit_completed(result=result,pars=pars,cpage=cpage,
392                                       qmin=qmin,qmax=qmax,
393                              ymin=ymin, ymax=ymax, xmin=xmin, xmax=xmax)
394            return
395        else:
396            wx.PostEvent(self.parent, StatusEvent(status="Simultaneous fit \
397            complete ", type="stop"))
398            ## fit more than 1 model at the same time
399            try:
400                for page, value in self.page_finder.iteritems():
401                    if value.get_scheduled()==1:
402                        list = value.get_model()
403                        model= list[0]
404                       
405                        small_out = []
406                        small_cov = []
407                        i = 0
408                        #Separate result in to data corresponding to each page
409                        for p in result.parameters:
410                            model_name,param_name = self.split_string(p.name) 
411                            if model.name == model_name:
412                                small_out.append(p.value )
413                                if p.stderr==None:
414                                    p.stderr=numpy.nan
415                                small_cov.append(p.stderr)
416                                model.setParam(param_name,p.value) 
417                               
418                        # Display result on each page
419                        page.onsetValues(result.fitness, small_out,small_cov)
420                        #Replot models
421                        msg= "Single Fit completed plotting %s:"%model.name
422                        wx.PostEvent(self.parent, StatusEvent(status="%s " % msg))
423                        self.plot_helper(currpage= page,qmin= qmin,qmax= qmax,
424                                         xmin=xmin, xmax=xmax,
425                                         ymin=ymin, ymax=ymax) 
426            except:
427                 msg= "Simultaneous Fit completed but Following error occurred: "
428                 wx.PostEvent(self.parent, StatusEvent(status="%s%s" %(msg,sys.exc_value)))
429                 return 
430             
431             
432    def stop_fit(self):
433        """
434            Stop the fit engine
435        """
436        if self.calc_thread != None and self.calc_thread.isrunning():
437            self.calc_thread.stop()
438            wx.PostEvent(self.parent, StatusEvent(status="Fitting  \
439                is cancelled" , type="stop"))
440           
441           
442    def _on_single_fit(self,id=None,qmin=None, qmax=None,ymin=None, ymax=None,xmin=None,xmax=None):
443        """
444            perform fit for the  current page  and return chisqr,out and cov
445            @param engineName: type of fit to be performed
446            @param id: unique id corresponding to a fit problem(model, set of data)
447            @param model: model to fit
448           
449        """
450        #set an engine to perform fit
451        from sans.fit.Fitting import Fit
452        self.fitter = Fit(self._fit_engine)
453        #Setting an id to store model and data in fit engine
454        self.fit_id = 0
455        if id!=None:
456            self.fit_id = id
457       
458        page_fitted = None
459        fit_problem = None
460        #Get information (model , data) related to the page on
461        #with the fit will be perform
462        current_pg= self.fit_panel.get_current_page() 
463        simul_pg= self.sim_page
464        pars=[]   
465        ## Check that the current page is different from self.sim_page
466        if current_pg != simul_pg:
467            value = self.page_finder[current_pg]
468            metadata =  value.get_fit_data()
469            list = value.get_model()
470            model = list[0]
471            smearer = value.get_smearer()
472           
473            #Create list of parameters for fitting used
474            templist=[]
475            try:
476                ## get the list of parameter names to fit
477                templist = current_pg.get_param_list()
478                for element in templist:
479                    pars.append(str(element[0].GetLabelText()))
480                pars.sort()
481                 
482                #Do the single fit
483                self.fitter.set_model(Model(model), self.fit_id, pars)
484                dy=[]
485                x=[]
486                y=[]
487                ## checking the validity of error
488                if metadata.__class__ in  ["Data1D","Theory1D"]:
489                    for i in range(len(metadata.dy)):
490                        if metadata.dy[i] !=0:
491                            dy.append(metadata.dy[i])
492                            x.append(metadata.x[i])
493                            y.append(metadata.y[i])
494                    if len(dy)>0:       
495                        metadata.dy=numpy.zeros(len(dy))
496                        metadata.dy=dy
497                        metadata.y=numpy.zeros(len(y))
498                        metadata.y=y
499                        metadata.x=numpy.zeros(len(x))
500                        metadata.x=x
501                       
502                self.fitter.set_data(data=metadata,Uid=self.fit_id,
503                                     smearer=smearer,qmin= qmin,qmax=qmax,
504                                     ymin=ymin,ymax=ymax)
505               
506                self.fitter.select_problem_for_fit(Uid= self.fit_id,
507                                                   value= value.get_scheduled())
508                page_fitted=current_pg
509               
510            except:
511                msg= "Single Fit error: %s" % sys.exc_value
512                wx.PostEvent(self.parent, StatusEvent(status= msg ))
513                return
514            # make sure to keep an alphabetic order
515            #of parameter names in the list     
516            try:
517                ## If a thread is already started, stop it
518                if self.calc_thread != None and self.calc_thread.isrunning():
519                    self.calc_thread.stop()
520                       
521                self.calc_thread =FitThread(parent =self.parent,
522                                            fn= self.fitter,
523                                            pars= pars,
524                                            cpage= page_fitted,
525                                           qmin=qmin,
526                                           qmax=qmax,
527                                         
528                                           completefn=self._single_fit_completed,
529                                           updatefn=None)
530                self.calc_thread.queue()
531                self.calc_thread.ready(2.5)
532             
533            except:
534                wx.PostEvent(self.parent, StatusEvent(status="Single Fit error: %s" % sys.exc_value))
535                return
536         
537    def _on_simul_fit(self, id=None,qmin=None,qmax=None, ymin=None, ymax=None):
538        """
539            perform fit for all the pages selected on simpage and return chisqr,out and cov
540            @param engineName: type of fit to be performed
541            @param id: unique id corresponding to a fit problem(model, set of data)
542             in park_integration
543            @param model: model to fit
544           
545        """
546        ##Setting an id to store model and data
547        self.fit_id= 0
548        #Setting an id to store model and data
549        if id!=None:
550             self.fit_id= id
551        ##  count the number of fitproblem schedule to fit
552        fitproblem_count= 0
553        for value in self.page_finder.itervalues():
554            if value.get_scheduled()==1:
555                fitproblem_count += 1
556        ## if simultaneous fit change automatically the engine to park
557        if fitproblem_count >1:
558            self._on_change_engine(engine='park')
559        from sans.fit.Fitting import Fit
560        self.fitter= Fit(self._fit_engine)
561       
562       
563        for page, value in self.page_finder.iteritems():
564            try:
565                if value.get_scheduled()==1:
566                    metadata = value.get_fit_data()
567                    list = value.get_model()
568                    model= list[0]
569                    ## store page
570                    cpage= page
571                    #Get list of parameters name to fit
572                    pars = []
573                    templist = []
574                    templist = page.get_param_list()
575                    for element in templist:
576                        try:
577                            name = str(element[0].GetLabelText())
578                            pars.append(name)
579                        except:
580                            wx.PostEvent(self.parent, StatusEvent(status="Simultaneous Fit error: %s" % sys.exc_value))
581                            return
582                    ## create a park model and reset parameter value if constraint
583                    ## is given
584                    new_model=Model(model)
585                    param=value.get_model_param()
586                   
587                    if len(param)>0:
588                        for item in param:
589                            param_value = item[1]
590                            param_name = item[0]
591   
592                            new_model.parameterset[ param_name].set( param_value )
593                       
594                    self.fitter.set_model(new_model, self.fit_id, pars) 
595                    ## check that non -zero value are send as dy in the fit engine
596                    dy=[]
597                    x=[]
598                    y=[]
599                    if metadata.__class__ in  ["Data1D","Theory1D"]:
600                        for i in range(len(metadata.dy)):
601                            if metadata.dy[i] !=0:
602                                dy.append(metadata.dy[i])
603                                x.append(metadata.x[i])
604                                y.append(metadata.y[i])
605                            if len(dy)>0:       
606                                metadata.dy=numpy.zeros(len(dy))
607                                metadata.dy=dy
608                                metadata.y=numpy.zeros(len(y))
609                                metadata.y=y
610                                metadata.x=numpy.zeros(len(x))
611                                metadata.x=x
612                               
613                    self.fitter.set_data(metadata,self.fit_id,qmin,qmax,ymin,ymax)
614                    self.fitter.select_problem_for_fit(Uid= self.fit_id,
615                                                       value= value.get_scheduled())
616                    self.fit_id += 1 
617                    value.clear_model_param()
618                   
619            except:
620                msg= "Simultaneous Fit error: %s" % sys.exc_value
621                wx.PostEvent(self.parent, StatusEvent(status= msg ))
622                return 
623           
624        #Do the simultaneous fit
625        try:
626            ## If a thread is already started, stop it
627            if self.calc_thread != None and self.calc_thread.isrunning():
628                self.calc_thread.stop()
629                        ## perform single fit
630            if  fitproblem_count==1:
631                self.calc_thread =FitThread(parent =self.parent,
632                                        fn= self.fitter,
633                                       qmin=qmin,
634                                       qmax=qmax,
635                                       ymin= ymin,
636                                       ymax= ymax,
637                                       cpage=cpage,
638                                       pars= pars,
639                                       completefn= self._simul_fit_completed,
640                                       updatefn=None)
641                     
642            else:
643                ## Perform more than 1 fit at the time
644                self.calc_thread =FitThread(parent =self.parent,
645                                        fn= self.fitter,
646                                       qmin=qmin,
647                                       qmax=qmax,
648                                       ymin= ymin,
649                                       ymax= ymax,
650                                       completefn= self._simul_fit_completed,
651                                       updatefn=None)
652            self.calc_thread.queue()
653            self.calc_thread.ready(2.5)
654           
655        except:
656            msg= "Simultaneous Fit error: %s" % sys.exc_value
657            wx.PostEvent(self.parent, StatusEvent(status= msg ))
658            return
659       
660    def _onset_engine_park(self,event):
661        """
662            set engine to park
663        """
664        self._on_change_engine('park')
665       
666       
667    def _onset_engine_scipy(self,event):
668        """
669            set engine to scipy
670        """
671        self._on_change_engine('scipy')
672       
673   
674    def _on_change_engine(self, engine='park'):
675        """
676            Allow to select the type of engine to perform fit
677            @param engine: the key work of the engine
678        """
679        self._fit_engine = engine
680        wx.PostEvent(self.parent, StatusEvent(status="Engine set to: %s" % self._fit_engine))
681   
682   
683    def _on_model_panel(self, evt):
684        """
685            react to model selection on any combo box or model menu.plot the model 
686            @param evt: wx.combobox event
687        """
688       
689        model = evt.model
690        name = evt.name
691       
692        sim_page=self.sim_page
693        current_pg = self.fit_panel.get_current_page() 
694        ## make sure nothing is done on self.sim_page
695        ## example trying to call set_panel on self.sim_page
696        if current_pg != sim_page:
697           
698            if len(self.page_finder[current_pg].get_model())==0:
699               
700                model.name="M"+str(self.index_model)
701                self.index_model += 1 
702            else:
703                model.name= self.page_finder[current_pg].get_model()[0].name
704               
705            try:
706                metadata=self.page_finder[current_pg].get_plotted_data()
707                M_name = model.name+"= "+name+"("+metadata.name+")"
708            except:
709                M_name = model.name+"= "+name
710            # save the name containing the data name with the appropriate model
711            self.page_finder[current_pg].set_model(model,M_name)
712           
713               
714            # save model name
715            self.plot_helper(currpage= current_pg,qmin= None,qmax= None)
716            if self.sim_page!=None:
717                self.sim_page.add_model(self.page_finder)
718       
719       
720       
721    def  set_smearer(self,smearer):
722        """
723            Get a smear object and store it to a fit problem
724            @param smearer: smear object to allow smearing data
725        """   
726        current_pg=self.fit_panel.get_current_page()
727        self.page_finder[current_pg].set_smearer(smearer)
728         
729         
730    def redraw_model(self,qmin= None,qmax= None):
731        """
732            Draw a theory according to model changes or data range.
733            @param qmin: the minimum value plotted for theory
734            @param qmax: the maximum value plotted for theory
735        """
736        current_pg=self.fit_panel.get_current_page()
737        for page, value in self.page_finder.iteritems():
738            if page ==current_pg :
739                break 
740        self.plot_helper(currpage=page,qmin= qmin,qmax= qmax)
741       
742       
743       
744    def plot_helper(self,currpage, qmin=None,qmax=None,
745                    ymin=None,ymax=None, xmin=None, xmax=None,title=None ):
746        """
747            Plot a theory given a model and data
748            @param model: the model from where the theory is derived
749            @param currpage: page in a dictionary referring to some data
750        """
751        if self.fit_panel.GetPageCount() >1:
752            for page in self.page_finder.iterkeys():
753                if  page == currpage : 
754                    data= self.page_finder[page].get_plotted_data()
755                    list= self.page_finder[page].get_model()
756                    smearer = self.page_finder[page].get_smearer()
757                    model = list[0]
758                    break 
759            ## create Model 1D
760            if data != None and data.__class__.__name__ != 'Data2D':
761                x= numpy.zeros(len(data.x)) 
762                y= numpy.zeros(len(data.y)) 
763               
764                theory = Theory1D(x=x, y=y)
765                theory.name = model.name
766                if hasattr(data ,"group_id"):
767                    theory.group_id = data.group_id
768                theory.id = "Model"
769               
770                x_name, x_units = data.get_xaxis() 
771                y_name, y_units = data.get_yaxis() 
772                theory.xaxis(x_name, x_units)
773                theory.yaxis(y_name, y_units)
774                if qmin == None :
775                    qmin = min(data.x)
776                if qmax == None :
777                    qmax = max(data.x)
778               
779                j=0
780                try:
781                    tempx = qmin
782                    tempy = model.run(qmin)
783                    theory.x[j]=tempx
784                    theory.y[j]=tempy
785                    j+=1
786                except :
787                        wx.PostEvent(self.parent, StatusEvent(status="fitting \
788                        skipping point x %g %s" %(qmin, sys.exc_value)))
789                           
790                for i in range(len(data.x)):
791                    try:
792                        if data.x[i]> qmin and data.x[i]< qmax: 
793                            tempx = data.x[i]
794                            tempy = model.run(tempx)
795                           
796                            if j< i:
797                                theory.x[j]=tempx
798                                theory.y[j]=tempy
799                                j+=1
800                           
801                    except:
802                        wx.PostEvent(self.parent, StatusEvent(status="fitting \
803                        skipping point x %g %s" %(data.x[i], sys.exc_value)))   
804                try:
805                    tempx = qmax
806                    tempy = model.run(qmax)
807               
808                    theory.x[j]=tempx
809                    theory.y[j]=tempy
810                    j+=1
811                except:
812                    wx.PostEvent(self.parent, StatusEvent(status="fitting \
813                        skipping point x %g %s" %(qmin, sys.exc_value)) )
814               
815                if smearer!=None:
816                    theory.y= smearer(theory.y)   
817                wx.PostEvent(self.parent, NewPlotEvent(plot=theory,
818                                                title=str(data.name)))
819            else:
820                ## create Model 2D
821                theory=Data2D(data.data, data.err_data)
822                theory.name= model.name
823                if title !=None:
824                    self.title = title
825                    theory.id= self.title
826                    theory.group_id= self.title+data.name
827                else:
828                    self.title= "Analytical model 2D "
829                    theory.id= "Model"
830                    theory.group_id= "Model"+data.name
831                theory.x_bins= data.x_bins
832                theory.y_bins= data.y_bins
833                tempy=[]
834               
835                if qmin==None:
836                    qmin=0
837                if qmax==None:
838                    x= math.pow(max(math.fabs(data.xmax),math.fabs(data.xmin)),2)
839                    y= math.pow(max(math.fabs(data.ymax),math.fabs(data.ymin)),2)
840                    qmax=math.sqrt( x+y )
841                   
842                if ymin==None:
843                    ymin=data.ymin
844                if ymax==None:
845                    ymax=data.ymax
846                if xmin ==None:
847                    xmin=data.xmin
848                if xmax==None:
849                    xmax=data.xmax     
850                                 
851                theory.data = numpy.zeros((len(data.y_bins),len(data.x_bins)))
852                for j in range(len(data.y_bins)):
853                    for i in range(len(data.x_bins)):
854                        tempqij=math.sqrt(( math.pow(data.y_bins[j],2)\
855                                           +math.pow(data.x_bins[i],2) ))
856                       
857                        if tempqij>= qmin and tempqij<= qmax: 
858                            theory.data[j][i] = model.runXY([data.y_bins[j],
859                                                             data.x_bins[i]] )
860                        else:
861                            ## Later, We need to decide which of  0 and None is better.
862                            theory.data[j][i]=0 
863             
864                theory.detector= data.detector
865                theory.source= data.source
866                ## qmin, qmax will used later to define q range.
867                theory.qmin= qmin
868                theory.qmax= qmax
869                ## plot boundaries
870                theory.ymin= ymin
871                theory.ymax= ymax
872                theory.xmin= xmin
873                theory.xmax= xmax
874       
875                wx.PostEvent(self.parent, NewPlotEvent(plot=theory,
876                                                title=self.title +str(data.name)))
877       
878       
879    def _on_model_menu(self, evt):
880        """
881            Plot a theory from a model selected from the menu
882            @param evt: wx.menu event
883        """
884        name = evt.model.__class__.__name__
885        if hasattr(evt.model, "name"):
886            name = evt.model.name
887        model=evt.model
888        description=model.description
889       
890        # Create a model page. If a new page is created, the model
891        # will be plotted automatically. If a page already exists,
892        # the content will be updated and the plot refreshed
893        self.fit_panel.add_model_page(model,description,name,topmenu=True)
894   
895    def complete1D_from_data(self, output,model, data, x, elapsed, name):
896        """
897             plot model 1D with data info
898        """
899        y= output
900        x= data.x
901        theory = Theory1D(x=x, y=y)
902        theory.name = model.name
903        theory.group_id = data.group_id
904        theory.id = "Model"
905       
906        x_name, x_units = data.get_xaxis() 
907        y_name, y_units = data.get_yaxis() 
908        theory.xaxis(x_name, x_units)
909        theory.yaxis(y_name, y_units)
910        wx.PostEvent(self.parent, NewPlotEvent(plot=theory,
911                                                title=str(data.name)))
912       
913    def draw_model1D_from_Data(self, data, model,name=None,
914                                qmin=None, qmax=None, smearer= None):
915        """
916            Draw model 1D from loaded data1D
917            @param data: loaded data
918            @param model: the model to plot
919        """
920        x = data.x
921        self.model= model
922        if qmin == None :
923           qmin = min(data.x)
924        if qmax == None :
925           qmax = max(data.x)
926        if name ==None:
927            name=model.name
928        try:
929            from model_thread import Calc1D
930            ## If a thread is already started, stop it
931            if self.calc_thread != None and self.calc_thread.isrunning():
932                self.calc_thread.stop()
933            self.calc_thread = Calc1D( x= x,
934                                      model= self.model, 
935                                       qmin= qmin,
936                                       qmax= qmax,
937                                       name= name,
938                                       smearer= smearer,
939                            completefn= self.complete1D_from_data,
940                            updatefn= self.update1D)
941            self.calc_thread.queue()
942           
943        except:
944            msg= " Error occurred when drawing %s Model 1D: "%self.model.name
945            msg+= " %s"%sys.exc_value
946            wx.PostEvent( self.parent, StatusEvent(status= msg ))
947            return 
948           
949    def draw_model(self, model, name, data= None, description= None,
950                   enable1D= True, enable2D= False,
951                   qmin= DEFAULT_QMIN, qmax= DEFAULT_QMAX, qstep= DEFAULT_NPTS):
952        """
953             Draw model.
954             @param model: the model to draw
955             @param name: the name of the model to draw
956             @param data: the data on which the model is based to be drawn
957             @param description: model's description
958             @param enable1D: if true enable drawing model 1D
959             @param enable2D: if true enable drawing model 2D
960             @param qmin:  Range's minimum value to draw model
961             @param qmax:  Range's maximum value to draw model
962             @param qstep: number of step to divide the x and y-axis
963             
964        """
965        ## draw model based on loaded data
966        if data !=None:
967            self.redraw_model(qmin,qmax)
968            return 
969        ## draw model 1D with no loaded data
970        self._draw_model1D(model,name,model.description, enable1D,qmin,qmax, qstep)
971        ## draw model 2D with no initial data
972        self._draw_model2D(model=model,
973                           description=model.description,
974                           enable2D= enable2D,
975                           qmin=qmin,
976                           qmax=qmax,
977                           qstep=qstep)
978       
979   
980    def update1D(self,x, output):
981        """
982            Update the output of plotting model 1D
983        """
984        self.calc_thread.ready(1)
985   
986    def complete1D(self, x,output, elapsed, name,data=None):
987        """
988            Complete plotting 1D data
989        """ 
990        try:
991            y = output
992            new_plot = Theory1D(x, y)
993            new_plot.name = name
994            new_plot.xaxis("\\rm{Q}", 'A^{-1}')
995            new_plot.yaxis("\\rm{Intensity} ","cm^{-1}")
996            new_plot.id ="Model"
997            new_plot.group_id ="Model"
998            # Pass the reset flag to let the plotting event handler
999            # know that we are replacing the whole plot
1000            wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot,
1001                             title="Analytical model 1D ", reset=True ))
1002           
1003        except:
1004            msg= " Error occurred when drawing %s Model 1D: "%new_plot.name
1005            msg+= " %s"%sys.exc_value
1006            wx.PostEvent( self.parent, StatusEvent(status= msg ))
1007            return 
1008       
1009                 
1010    def _draw_model1D(self, model, name, description= None, enable1D= True,
1011                      qmin= DEFAULT_QMIN, qmax= DEFAULT_QMAX, qstep= DEFAULT_NPTS):
1012        """
1013            Draw model 1D between qmin and qmax value
1014            @param model: the model to draw
1015            @param name: the name of the model
1016            @param description: descripion of the model as string
1017            @param enable1D:  allow to draw model 1D if True  else False
1018            @param qmin: the minimum value of the range to draw the model
1019            @param qmax: the maximum value of the range to draw the model
1020            @param qstep: the number of points to draw 
1021        """
1022        x=  numpy.linspace(start= qmin,
1023                           stop= qmax,
1024                           num= qstep,
1025                           endpoint=True
1026                           )     
1027        self.model= model
1028        if enable1D:
1029            try:
1030                from model_thread import Calc1D
1031                ## If a thread is already started, stop it
1032                if self.calc_thread != None and self.calc_thread.isrunning():
1033                    self.calc_thread.stop()
1034                self.calc_thread = Calc1D( x= x,
1035                                          model= self.model, 
1036                                           qmin= qmin,
1037                                           qmax= qmax,
1038                                           name= name,
1039                                           data=None,
1040                                        smearer=None,
1041                                completefn= self.complete1D,
1042                                updatefn= self.update1D)
1043                self.calc_thread.queue()
1044               
1045            except:
1046                msg= " Error occurred when drawing %s Model 1D: "%self.model.name
1047                msg+= " %s"%sys.exc_value
1048                wx.PostEvent( self.parent, StatusEvent(status= msg ))
1049                return 
1050               
1051
1052
1053       
1054    def update2D(self, output,time=None):
1055        """
1056            Update the output of plotting model
1057        """
1058        wx.PostEvent(self.parent, StatusEvent(status="Plot \
1059        #updating ... ",type="update"))
1060        self.calc_thread.ready(0.01)
1061       
1062       
1063    def complete2D(self, output, elapsed, model, qmin, qmax,qstep=DEFAULT_NPTS):
1064        """
1065            Complete get the result of modelthread and create model 2D
1066            that can be plot.
1067        """
1068        msg = "Calc complete !"
1069        wx.PostEvent( self.parent, StatusEvent( status= msg , type="stop" ))
1070   
1071        data = output
1072        temp= numpy.zeros(numpy.shape(data))
1073        temp[temp==0]=1
1074        theory= Data2D(image=data, err_image=temp)
1075   
1076        from DataLoader.data_info import Detector, Source
1077       
1078        detector = Detector()
1079        theory.detector=[]
1080        theory.detector.append(detector) 
1081           
1082        theory.detector[0].distance=1e+32
1083        theory.source= Source()
1084        theory.source.wavelength=2*math.pi/1e+32
1085        theory.x_bins =[]
1086        theory.y_bins =[]
1087        ## Create detector for Model 2D
1088        xmax=2*theory.detector[0].distance*math.atan(\
1089                            qmax/(4*math.pi/theory.source.wavelength))
1090       
1091        theory.detector[0].pixel_size.x= xmax/(qstep/2-0.5)
1092        theory.detector[0].pixel_size.y= xmax/(qstep/2-0.5)
1093        theory.detector[0].beam_center.x= qmax
1094        theory.detector[0].beam_center.y= qmax
1095        ## create x_bins and y_bins of the model 2D
1096        distance   = theory.detector[0].distance
1097        pixel      = qstep/2-1
1098        theta      = pixel / distance / qstep#100.0
1099        wavelength = theory.source.wavelength
1100        pixel_width_x = theory.detector[0].pixel_size.x
1101        pixel_width_y = theory.detector[0].pixel_size.y
1102        center_x      = theory.detector[0].beam_center.x/pixel_width_x
1103        center_y      = theory.detector[0].beam_center.y/pixel_width_y
1104       
1105       
1106        size_x, size_y= numpy.shape(theory.data)
1107        for i_x in range(size_x):
1108            theta = (i_x-center_x)*pixel_width_x / distance
1109            qx = 4.0*math.pi/wavelength * math.tan(theta/2.0)
1110            theory.x_bins.append(qx)   
1111        for i_y in range(size_y):
1112            theta = (i_y-center_y)*pixel_width_y / distance
1113            qy =4.0*math.pi/wavelength * math.tan(theta/2.0)
1114            theory.y_bins.append(qy)
1115           
1116        theory.name= model.name
1117        theory.group_id ="Model"
1118        theory.id ="Model"
1119        ## determine plot boundaries
1120        theory.xmin= -qmax
1121        theory.xmax= qmax
1122        theory.ymin= -qmax
1123        theory.ymax= qmax
1124        ## plot
1125        wx.PostEvent(self.parent, NewPlotEvent(plot=theory,
1126                         title="Analytical model 2D ", reset=True ))
1127         
1128       
1129         
1130    def _draw_model2D(self,model,description=None, enable2D=False,
1131                      qmin=DEFAULT_QMIN, qmax=DEFAULT_QMAX, qstep=DEFAULT_NPTS):
1132        """
1133            draw model in 2D
1134            @param model: instance of the model to draw
1135            @param description: the description of the model
1136            @param enable2D: when True allows to draw model 2D
1137            @param qmin: the minimum value to  draw model 2D
1138            @param qmax: the maximum value to draw model 2D
1139            @param qstep: the number of division of Qx and Qy of the model to draw
1140           
1141        """
1142        x=  numpy.linspace(start= -1*qmax,
1143                               stop= qmax,
1144                               num= qstep,
1145                               endpoint=True ) 
1146        y = numpy.linspace(start= -1*qmax,
1147                               stop= qmax,
1148                               num= qstep,
1149                               endpoint=True )
1150       
1151        self.model= model
1152        if enable2D:
1153            try:
1154                from model_thread import Calc2D
1155                ## If a thread is already started, stop it
1156                if self.calc_thread != None and self.calc_thread.isrunning():
1157                    self.calc_thread.stop()
1158                self.calc_thread = Calc2D( x= x,
1159                                           y= y, model= self.model, 
1160                                           qmin= qmin,
1161                                           qmax= qmax,
1162                                           qstep= qstep,
1163                                completefn= self.complete2D,
1164                                updatefn= self.update2D )
1165                self.calc_thread.queue()
1166               
1167            except:
1168                raise
1169   
1170   
1171if __name__ == "__main__":
1172    i = Plugin()
1173   
1174   
1175   
1176   
Note: See TracBrowser for help on using the repository browser.