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

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

comment added on fitting

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