source: sasview/fittingview/src/sans/perspectives/fitting/fitting.py @ 4310dbb

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 4310dbb was 33b7954, checked in by Jae Cho <jhjcho@…>, 13 years ago

fixed a bug; not plotting 2D res on list of 1D and 2D data sets are sent to a fitpage at once

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