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

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 2b878d74 was 2b878d74, checked in by Jae Cho <jhjcho@…>, 12 years ago

spelling correction

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