source: sasview/fittingview/src/sans/perspectives/fitting/fitting.py @ 4a4164c

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 4a4164c was 356d2d3, checked in by Jae Cho <jhjcho@…>, 13 years ago

bug fix: error display in Grid

  • Property mode set to 100644
File size: 89.7 KB
Line 
1
2
3################################################################################
4#This software was developed by the University of Tennessee as part of the
5#Distributed Data Analysis of Neutron Scattering Experiments (DANSE)
6#project funded by the US National Science Foundation.
7#
8#See the license text in license.txt
9#
10#copyright 2009, University of Tennessee
11################################################################################
12
13
14import re
15import sys
16import os
17import wx
18import logging
19import numpy
20import string
21import time
22from copy import deepcopy
23import models
24import fitpage
25
26from sans.dataloader.loader import Loader
27from sans.guiframe.dataFitting import Data2D
28from sans.guiframe.dataFitting import Data1D
29from sans.guiframe.dataFitting import check_data_validity
30from sans.guiframe.events import NewPlotEvent
31from sans.guiframe.events import StatusEvent 
32from sans.guiframe.events import EVT_SLICER_PANEL
33from sans.guiframe.events import EVT_SLICER_PARS_UPDATE
34from sans.guiframe.gui_style import GUIFRAME_ID
35from sans.guiframe.plugin_base import PluginBase
36from sans.guiframe.data_processor import BatchCell
37from sans.fit.Fitting import Fit
38from .console import ConsoleUpdate
39from .fitproblem import FitProblemDictionary
40from .fitpanel import FitPanel
41from .fit_thread import FitThread
42from .pagestate import Reader
43from .fitpage import Chi2UpdateEvent
44from 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                            else:
1444                                batch_inputs["error on %s" % param].append('-')
1445                            model.setParam(param, res.pvec[index])
1446                #fill the batch result with emtpy value if not in the current
1447                #model
1448                EMPTY = "-"
1449                for key in batch_outputs.keys():
1450                    if key not in param_list and key not in ["Chi2", "Data"]:
1451                        batch_outputs[key].append(EMPTY)
1452                #for key in batch_inputs.keys():
1453                    #if key not in param_list and key not in ["Chi2", "Data"]:
1454                        #batch_inputs[key].append(EMPTY)
1455                               
1456                self.page_finder[pid].set_batch_result(batch_inputs=batch_inputs,
1457                                                     batch_outputs=batch_outputs) 
1458               
1459                cpage = self.fit_panel.get_page_by_id(pid)
1460                cpage._on_fit_complete()
1461                self.page_finder[pid][data.id].set_result(res)
1462                fitproblem = self.page_finder[pid][data.id]
1463                qmin, qmax = fitproblem.get_range()
1464               
1465                if correct_result:
1466                    if not is_data2d:
1467                        self._complete1D(x=data.x, y=res.theory, page_id=pid, 
1468                                     elapsed=None, 
1469                                     index=res.index, model=model,
1470                                     weight=None, fid=data.id,
1471                                     toggle_mode_on=False, state=None, 
1472                                     data=data, update_chisqr=False, 
1473                                     source='fit')
1474                    else:
1475                        self._complete2D(image=new_theory, data=data,
1476                                      model=model,
1477                                      page_id=pid,  elapsed=None, 
1478                                      index=res.index, 
1479                                      qmin=qmin,
1480                                     qmax=qmax, fid=data.id, weight=None,
1481                                      toggle_mode_on=False, state=None, 
1482                                     update_chisqr=False, 
1483                                     source='fit')
1484                self.on_set_batch_result(page_id=pid, 
1485                                         fid=data.id, 
1486                                         batch_outputs=batch_outputs, 
1487                                         batch_inputs=batch_inputs)
1488       
1489        wx.PostEvent(self.parent, StatusEvent(status=msg, error="error",
1490                                                              type="stop"))
1491        wx.CallAfter(self.parent.on_set_batch_result,batch_outputs, 
1492                                            batch_inputs,
1493                                           self.sub_menu)
1494       
1495    def on_set_batch_result(self, page_id, fid, batch_outputs, batch_inputs):
1496        """
1497        """
1498       
1499        pid =  page_id
1500        if fid not in self.page_finder[pid]:
1501            return
1502        fitproblem = self.page_finder[pid][fid]
1503        index = self.page_finder[pid].nbr_residuals_computed - 1
1504        residuals =  fitproblem.get_residuals()
1505        theory_data = fitproblem.get_theory_data()
1506        data = fitproblem.get_fit_data()
1507        model = fitproblem.get_model()
1508        #fill batch result information
1509        if "Data" not in batch_outputs.keys():
1510            batch_outputs["Data"] = []
1511        from sans.guiframe.data_processor import BatchCell
1512        cell = BatchCell()
1513        cell.label = data.name
1514        cell.value = index
1515       
1516        if theory_data != None:
1517            #Suucessful fit
1518            theory_data.id = wx.NewId()
1519            theory_data.name = model.name + "[%s]" % str(data.name)
1520            if issubclass(theory_data.__class__, Data2D):
1521                group_id = wx.NewId()
1522                theory_data.group_id = group_id
1523                if group_id not in theory_data.list_group_id:
1524                    theory_data.list_group_id.append(group_id)
1525               
1526            try:
1527                # associate residuals plot
1528                if issubclass(residuals.__class__, Data2D):
1529                    group_id = wx.NewId()
1530                    residuals.group_id = group_id
1531                    if group_id not in residuals.list_group_id:
1532                        residuals.list_group_id.append(group_id)
1533                batch_outputs["Chi2"][index].object = [residuals]
1534            except:
1535                pass
1536
1537        cell.object = [data, theory_data]
1538        batch_outputs["Data"].append(cell)
1539        for key, value in data.meta_data.iteritems():
1540            if key not in batch_inputs.keys():
1541                batch_inputs[key] = []
1542            #if key.lower().strip() != "loader":
1543            batch_inputs[key].append(value)
1544        param = "temperature"
1545        if hasattr(data.sample, param):
1546            if param not in  batch_inputs.keys():
1547                 batch_inputs[param] = []
1548            batch_inputs[param].append(data.sample.temperature)
1549       
1550
1551    def _fit_completed(self, result, page_id, batch_outputs,
1552                             batch_inputs=None,
1553                              pars=None, 
1554                             elapsed=None):
1555        """
1556        Display result of the fit on related panel(s).
1557        :param result: list of object generated when fit ends
1558        :param pars: list of names of parameters fitted
1559        :param page_id: list of page ids which called fit function
1560        :param elapsed: time spent at the fitting level
1561        """
1562        t1 = time.time()
1563        str_time = time.strftime("%a, %d %b %Y %H:%M:%S ", time.localtime(t1))
1564        msg = "Fit completed on %s \n" % str_time
1565        msg += "Duration time: %s s.\n" % str(elapsed)
1566        wx.PostEvent(self.parent, StatusEvent(status=msg, info="info",
1567                                                      type="stop"))
1568        # reset fit_engine if changed by simul_fit
1569        if self._fit_engine != self._gui_engine:
1570            self._on_change_engine(self._gui_engine)
1571        self._update_fit_button(page_id)
1572        result = result[0]
1573        self.fit_thread_list = {}
1574        if page_id is None:
1575            page_id = []
1576        ## fit more than 1 model at the same time
1577        self._mac_sleep(0.2) 
1578        try:
1579            index = 0
1580            for uid in page_id:
1581                res = result[index]
1582                if res.fitness is None or \
1583                    not numpy.isfinite(res.fitness) or \
1584                    numpy.any(res.pvec == None) or \
1585                    not numpy.all(numpy.isfinite(res.pvec)):
1586                    msg = "Fitting did not converge!!!"
1587                    wx.PostEvent(self.parent, 
1588                             StatusEvent(status=msg, 
1589                                         info="warning",
1590                                         type="stop"))
1591                    self._update_fit_button(page_id)
1592                else:
1593                    #set the panel when fit result are float not list
1594                    if res.pvec.__class__== numpy.float64:
1595                        pvec = [res.pvec]
1596                    else:
1597                        pvec = res.pvec
1598                    if res.stderr.__class__== numpy.float64:
1599                        stderr = [res.stderr]
1600                    else:
1601                        stderr = res.stderr
1602                    cpage = self.fit_panel.get_page_by_id(uid)
1603                    # Make sure we got all results
1604                    #(CallAfter is important to MAC)
1605                    wx.CallAfter(cpage.onsetValues, res.fitness, res.param_list, 
1606                             pvec, stderr)
1607                    index += 1
1608                    cpage._on_fit_complete()
1609        except:
1610            msg = "Fit completed but Following"
1611            msg += " error occurred:%s" % sys.exc_value
1612            wx.PostEvent(self.parent, StatusEvent(status=msg, info="error",
1613                                                  type="stop"))
1614           
1615    def _update_fit_button(self, page_id):
1616        """
1617        Update Fit button when fit stopped
1618       
1619        : parameter page_id: fitpage where the button is
1620        """
1621        if page_id.__class__.__name__ != 'list':
1622            page_id = [page_id]
1623        for uid in page_id: 
1624            page = self.fit_panel.get_page_by_id(uid)
1625            page._on_fit_complete()
1626       
1627    def _on_show_panel(self, event):
1628        """
1629        """
1630        pass
1631   
1632    def on_reset_batch_flag(self, event):
1633        """
1634        Set batch_reset_flag
1635        """
1636        event.Skip()
1637        if self.menu1 == None:
1638            return
1639        menu_item = self.menu1.FindItemById(self.id_reset_flag)
1640        flag = menu_item.IsChecked()
1641        if not flag:
1642            menu_item.Check(False)
1643            self.batch_reset_flag = True
1644        else:
1645            menu_item.Check(True)
1646            self.batch_reset_flag = False
1647       
1648        ## post a message to status bar
1649        msg = "Set Chain Fitting: %s" % str(not self.batch_reset_flag)
1650        wx.PostEvent(self.parent, 
1651                     StatusEvent(status=msg))
1652
1653    def _onset_engine_park(self,event):
1654        """
1655        set engine to park
1656        """
1657        self._on_change_engine('park')
1658       
1659    def _onset_engine_scipy(self,event):
1660        """
1661        set engine to scipy
1662        """
1663        self._on_change_engine('scipy')
1664       
1665    def _on_slicer_event(self, event):
1666        """
1667        Receive a panel as event and send it to guiframe
1668       
1669        :param event: event containing a panel
1670       
1671        """
1672        if event.panel is not None:
1673            new_panel = event.panel
1674            self.slicer_panels.append(event.panel)
1675            # Set group ID if available
1676            event_id = self.parent.popup_panel(new_panel)
1677            new_panel.uid = event_id
1678            self.mypanels.append(new_panel) 
1679       
1680    def _onclearslicer(self, event):
1681        """
1682        Clear the boxslicer when close the panel associate with this slicer
1683        """
1684        name =event.GetPane().caption
1685   
1686        for panel in self.slicer_panels:
1687            if panel.window_caption==name:
1688               
1689                for item in self.parent.panels:
1690                    if hasattr(self.parent.panels[item], "uid"):
1691                        if self.parent.panels[item].uid ==panel.base.uid:
1692                            self.parent.panels[item].onClearSlicer(event)
1693                            self.parent._mgr.Update()
1694                            break 
1695                break
1696   
1697    def _return_engine_type(self):
1698        """
1699        return the current type of engine
1700        """
1701        return self._fit_engine
1702     
1703     
1704    def _on_change_engine(self, engine='park'):
1705        """
1706        Allow to select the type of engine to perform fit
1707       
1708        :param engine: the key work of the engine
1709       
1710        """
1711        ## saving fit engine name
1712        self._fit_engine = engine
1713        ## change menu item state
1714        if engine == "park":
1715            self.menu1.FindItemById(self.park_id).Check(True)
1716            self.menu1.FindItemById(self.scipy_id).Check(False)
1717        else:
1718            self.menu1.FindItemById(self.park_id).Check(False)
1719            self.menu1.FindItemById(self.scipy_id).Check(True)
1720        ## post a message to status bar
1721        msg = "Engine set to: %s" % self._fit_engine
1722        wx.PostEvent(self.parent, 
1723                     StatusEvent(status=msg))
1724        ## send the current engine type to fitpanel
1725        self.fit_panel._on_engine_change(name=self._fit_engine)
1726
1727       
1728    def _on_model_panel(self, evt):
1729        """
1730        react to model selection on any combo box or model menu.plot the model 
1731       
1732        :param evt: wx.combobox event
1733       
1734        """
1735        model = evt.model
1736        uid = evt.uid
1737        qmin = evt.qmin
1738        qmax = evt.qmax
1739        smearer = evt.smearer
1740        caption = evt.caption
1741        enable_smearer = evt.enable_smearer
1742        if model == None:
1743            return
1744        if uid not in self.page_finder.keys():
1745            return
1746        # save the name containing the data name with the appropriate model
1747        self.page_finder[uid].set_model(model)
1748        self.page_finder[uid].enable_smearing(enable_smearer)
1749        self.page_finder[uid].set_range(qmin=qmin, qmax=qmax)
1750        self.page_finder[uid].set_fit_tab_caption(caption=caption)
1751        if self.sim_page is not None and not self.batch_on:
1752            self.sim_page.draw_page()
1753        if self.batch_page is not None and self.batch_on:
1754            self.batch_page.draw_page()
1755       
1756    def _update1D(self, x, output):
1757        """
1758        Update the output of plotting model 1D
1759        """
1760        msg = "Plot updating ... "
1761        wx.PostEvent(self.parent, StatusEvent(status=msg,type="update"))
1762       
1763    def _complete1D(self, x, y, page_id, elapsed, index, model,
1764                    weight=None, fid=None,
1765                    toggle_mode_on=False, state=None, 
1766                    data=None, update_chisqr=True, source='model'):
1767        """
1768        Complete plotting 1D data
1769        """ 
1770        try:
1771            numpy.nan_to_num(y)
1772           
1773            new_plot = Data1D(x=x, y=y)
1774            new_plot.is_data = False
1775            new_plot.dy = numpy.zeros(len(y))
1776            new_plot.symbol = GUIFRAME_ID.CURVE_SYMBOL_NUM
1777            _yaxis, _yunit = data.get_yaxis() 
1778            _xaxis, _xunit = data.get_xaxis() 
1779            new_plot.title = data.name
1780
1781            new_plot.group_id = data.group_id#self.page_finder[page_id].get_graph_id()
1782            if new_plot.group_id == None:
1783                new_plot.group_id = data.group_id
1784            new_plot.id =  str(page_id) + "model"
1785            #if new_plot.id in self.color_dict:
1786            #    new_plot.custom_color = self.color_dict[new_plot.id]
1787            #find if this theory was already plotted and replace that plot given
1788            #the same id
1789            theory_data = self.page_finder[page_id].get_theory_data(fid=data.id)
1790           
1791            if data.is_data:
1792                data_name = str(data.name)
1793            else:
1794                data_name = str(model.__class__.__name__)
1795           
1796            new_plot.name = model.name + " ["+ data_name +"]"
1797            new_plot.xaxis(_xaxis, _xunit)
1798            new_plot.yaxis(_yaxis, _yunit)
1799            self.page_finder[page_id].set_theory_data(data=new_plot, 
1800                                                      fid=data.id)
1801            self.parent.update_theory(data_id=data.id, theory=new_plot,
1802                                       state=state)   
1803            current_pg = self.fit_panel.get_page_by_id(page_id)
1804            title = new_plot.title
1805            batch_on = self.fit_panel.get_page_by_id(page_id).batch_on
1806            if not batch_on:
1807                wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot,
1808                                            title=str(title)))
1809            else:
1810                top_data_id = self.fit_panel.get_page_by_id(page_id).data.id
1811                if data.id == top_data_id:
1812                    wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot,
1813                                            title=str(title)))   
1814            caption = current_pg.window_caption
1815            self.page_finder[page_id].set_fit_tab_caption(caption=caption)
1816           
1817            self.page_finder[page_id].set_theory_data(data=new_plot, 
1818                                                      fid=data.id)
1819            if toggle_mode_on:
1820                wx.PostEvent(self.parent, 
1821                             NewPlotEvent(group_id=str(page_id) + " Model2D",
1822                                               action="Hide"))
1823            else:
1824                if update_chisqr:
1825                    wx.PostEvent(current_pg,
1826                                 Chi2UpdateEvent(output=self._cal_chisqr(
1827                                                                data=data,
1828                                                                fid=fid,
1829                                                                weight=weight,
1830                                                            page_id=page_id,
1831                                                            index=index)))
1832                else:
1833                    self._plot_residuals(page_id=page_id, data=data, fid=fid,
1834                                          index=index, weight=weight)
1835
1836            msg = "Computation  completed!"
1837            wx.PostEvent( self.parent, StatusEvent(status=msg, type="stop" ))
1838        except:
1839            raise
1840            #msg = " Error occurred when drawing %s Model 1D: " % new_plot.name
1841            #msg += " %s"  % sys.exc_value
1842            #wx.PostEvent(self.parent, StatusEvent(status=msg, type="stop"))
1843   
1844    def _update2D(self, output,time=None):
1845        """
1846        Update the output of plotting model
1847        """
1848        wx.PostEvent(self.parent, StatusEvent(status="Plot \
1849        #updating ... ", type="update"))
1850        #self.ready_fit()
1851 
1852    def _complete2D(self, image, data, model, page_id,  elapsed, index, qmin,
1853                qmax, fid=None, weight=None, toggle_mode_on=False, state=None, 
1854                     update_chisqr=True, source='model'):
1855        """
1856        Complete get the result of modelthread and create model 2D
1857        that can be plot.
1858        """
1859        numpy.nan_to_num(image)
1860        new_plot= Data2D(image=image, err_image=data.err_data)
1861        new_plot.name = model.name
1862        new_plot.title = "Analytical model 2D "
1863        new_plot.id = str(page_id) + "model"
1864        new_plot.group_id = str(page_id) + " Model2D"
1865        new_plot.detector = data.detector
1866        new_plot.source = data.source
1867        new_plot.is_data = False 
1868        new_plot.qx_data = data.qx_data
1869        new_plot.qy_data = data.qy_data
1870        new_plot.q_data = data.q_data
1871        new_plot.mask = data.mask
1872        ## plot boundaries
1873        new_plot.ymin = data.ymin
1874        new_plot.ymax = data.ymax
1875        new_plot.xmin = data.xmin
1876        new_plot.xmax = data.xmax
1877        title = data.title
1878       
1879        new_plot.is_data = False
1880        if data.is_data:
1881            data_name = str(data.name)
1882        else:
1883            data_name = str(model.__class__.__name__)
1884
1885        if len(title) > 1:
1886            new_plot.title = "Model2D for " + data_name
1887        new_plot.name = model.name + " [" + \
1888                                    data_name + "-2D]"
1889        theory_data = deepcopy(new_plot)
1890        theory_data.name = "Unknown"
1891       
1892        self.page_finder[page_id].set_theory_data(data=theory_data, fid=data.id)
1893        self.parent.update_theory(data_id=data.id, 
1894                                       theory=new_plot,
1895                                       state=state) 
1896        current_pg = self.fit_panel.get_page_by_id(page_id)
1897        title = new_plot.title
1898        batch_on = self.fit_panel.get_page_by_id(page_id).batch_on
1899        if not source == 'fit':
1900            wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot,
1901                                               title=title))
1902        self.page_finder[page_id].set_theory_data(data=new_plot, fid=data.id)
1903        if toggle_mode_on:
1904            wx.PostEvent(self.parent, 
1905                             NewPlotEvent(group_id=str(page_id) + " Model1D",
1906                                               action="Hide"))
1907        else:
1908            # Chisqr in fitpage
1909            if update_chisqr:
1910                wx.PostEvent(current_pg,
1911                             Chi2UpdateEvent(output=self._cal_chisqr(data=data,
1912                                                                    weight=weight,
1913                                                                    fid=fid,
1914                                                         page_id=page_id,
1915                                                         index=index)))
1916            else:
1917                self._plot_residuals(page_id=page_id, data=data, fid=fid,
1918                                      index=index, weight=weight)
1919        msg = "Computation  completed!"
1920        wx.PostEvent(self.parent, StatusEvent(status=msg, type="stop"))
1921   
1922    def _draw_model2D(self, model, page_id, qmin,
1923                      qmax,
1924                      data=None, smearer=None,
1925                      description=None, enable2D=False,
1926                      state=None,
1927                      fid=None,
1928                      weight=None,
1929                      toggle_mode_on=False,
1930                       update_chisqr=True, source='model'):
1931        """
1932        draw model in 2D
1933       
1934        :param model: instance of the model to draw
1935        :param description: the description of the model
1936        :param enable2D: when True allows to draw model 2D
1937        :param qmin: the minimum value to  draw model 2D
1938        :param qmax: the maximum value to draw model 2D
1939        :param qstep: the number of division of Qx and Qy of the model to draw
1940           
1941        """
1942        if not enable2D:
1943            return None
1944        try:
1945            from model_thread import Calc2D
1946            ## If a thread is already started, stop it
1947            if (self.calc_2D is not None) and self.calc_2D.isrunning():
1948                self.calc_2D.stop()
1949            self.calc_2D = Calc2D(model=model, 
1950                                    data=data,
1951                                    page_id=page_id,
1952                                    smearer=smearer,
1953                                    qmin=qmin,
1954                                    qmax=qmax,
1955                                    weight=weight, 
1956                                    fid=fid,
1957                                    toggle_mode_on=toggle_mode_on,
1958                                    state=state,
1959                                    completefn=self._complete2D,
1960                                    update_chisqr=update_chisqr, source=source)
1961            self.calc_2D.queue()
1962
1963        except:
1964            raise
1965            #msg = " Error occurred when drawing %s Model 2D: " % model.name
1966            #msg += " %s" % sys.exc_value
1967            #wx.PostEvent(self.parent, StatusEvent(status=msg))
1968
1969    def _draw_model1D(self, model, page_id, data, 
1970                      qmin, qmax, smearer=None,
1971                state=None,
1972                weight=None,
1973                fid=None, 
1974                toggle_mode_on=False, update_chisqr=True, source='model',
1975                enable1D=True):
1976        """
1977        Draw model 1D from loaded data1D
1978       
1979        :param data: loaded data
1980        :param model: the model to plot
1981       
1982        """
1983        if not enable1D:
1984            return 
1985        try:
1986            from model_thread import Calc1D
1987            ## If a thread is already started, stop it
1988            if (self.calc_1D is not None) and self.calc_1D.isrunning():
1989                self.calc_1D.stop()
1990            self.calc_1D = Calc1D(data=data,
1991                                  model=model,
1992                                  page_id=page_id, 
1993                                  qmin=qmin,
1994                                  qmax=qmax,
1995                                  smearer=smearer,
1996                                  state=state,
1997                                  weight=weight,
1998                                  fid=fid,
1999                                  toggle_mode_on=toggle_mode_on,
2000                                  completefn=self._complete1D,
2001                                  #updatefn = self._update1D,
2002                                  update_chisqr=update_chisqr,
2003                                  source=source)
2004            self.calc_1D.queue()
2005        except:
2006            msg = " Error occurred when drawing %s Model 1D: " % model.name
2007            msg += " %s" % sys.exc_value
2008            wx.PostEvent(self.parent, StatusEvent(status=msg))
2009   
2010 
2011   
2012    def _cal_chisqr(self, page_id, data, weight, fid=None, index=None): 
2013        """
2014        Get handy Chisqr using the output from draw1D and 2D,
2015        instead of calling expansive CalcChisqr in guithread
2016        """
2017        data_copy = deepcopy(data) 
2018        # default chisqr
2019        chisqr = None
2020        #to compute chisq make sure data has valid data
2021        # return None if data == None
2022        if not check_data_validity(data_copy) or data_copy == None:
2023            return chisqr
2024
2025        # Get data: data I, theory I, and data dI in order
2026        if data_copy.__class__.__name__ == "Data2D":
2027            if index == None: 
2028                index = numpy.ones(len(data_copy.data),ntype=bool)
2029            if weight != None:
2030                data_copy.err_data = weight
2031            # get rid of zero error points
2032            index = index & (data_copy.err_data != 0) 
2033            index = index & (numpy.isfinite(data_copy.data)) 
2034            fn = data_copy.data[index] 
2035            theory_data = self.page_finder[page_id].get_theory_data(fid=data_copy.id)
2036            if theory_data== None:
2037                return chisqr
2038            gn = theory_data.data[index]
2039            en = data_copy.err_data[index]
2040        else:
2041            # 1 d theory from model_thread is only in the range of index
2042            if index == None: 
2043                index = numpy.ones(len(data_copy.y), ntype=bool)
2044            if weight != None:
2045                data_copy.dy = weight
2046            if data_copy.dy == None or data_copy.dy == []:
2047                dy = numpy.ones(len(data_copy.y))
2048            else:
2049                ## Set consitently w/AbstractFitengine:
2050                # But this should be corrected later.
2051                dy = deepcopy(data_copy.dy)
2052                dy[dy==0] = 1
2053            fn = data_copy.y[index] 
2054           
2055            theory_data = self.page_finder[page_id].get_theory_data(fid=data_copy.id)
2056            if theory_data== None:
2057                return chisqr
2058            gn = theory_data.y
2059            en = dy[index]
2060           
2061        # residual
2062        res = (fn - gn) / en
2063        residuals = res[numpy.isfinite(res)]
2064        # get chisqr only w/finite
2065        chisqr = numpy.average(residuals * residuals)
2066       
2067        self._plot_residuals(page_id=page_id, data=data_copy, 
2068                             fid=fid,
2069                             weight=weight, index=index)
2070       
2071        return chisqr
2072   
2073    def _plot_residuals(self, page_id, weight, fid=None,
2074                        data=None, index=None): 
2075        """
2076        Plot the residuals
2077       
2078        :param data: data
2079        :param index: index array (bool)
2080        : Note: this is different from the residuals in cal_chisqr()
2081        """
2082        data_copy = deepcopy(data)
2083        # Get data: data I, theory I, and data dI in order
2084        if data_copy.__class__.__name__ == "Data2D":
2085            # build residuals
2086            residuals = Data2D()
2087            #residuals.copy_from_datainfo(data)
2088            # Not for trunk the line below, instead use the line above
2089            data_copy.clone_without_data(len(data_copy.data), residuals)
2090            residuals.data = None
2091            fn = data_copy.data#[index]
2092            theory_data = self.page_finder[page_id].get_theory_data(fid=data_copy.id)
2093            gn = theory_data.data#[index]
2094            if weight == None:
2095                en = data_copy.err_data
2096            else:
2097                en = weight
2098            residuals.data = (fn - gn) / en
2099            residuals.qx_data = data_copy.qx_data#[index]
2100            residuals.qy_data = data_copy.qy_data #[index]
2101            residuals.q_data = data_copy.q_data#[index]
2102            residuals.err_data = numpy.ones(len(residuals.data))#[index]
2103            residuals.xmin = min(residuals.qx_data)
2104            residuals.xmax = max(residuals.qx_data)
2105            residuals.ymin = min(residuals.qy_data)
2106            residuals.ymax = max(residuals.qy_data)
2107            residuals.q_data = data_copy.q_data#[index]
2108            residuals.mask = data_copy.mask
2109            residuals.scale = 'linear'
2110            # check the lengths
2111            if len(residuals.data) != len(residuals.q_data):
2112                return
2113        else:
2114            # 1 d theory from model_thread is only in the range of index
2115            if data_copy.dy == None or data_copy.dy == []:
2116                dy = numpy.ones(len(data_copy.y))
2117            else:
2118                if weight == None:
2119                    dy = numpy.ones(len(data_copy.y))
2120                ## Set consitently w/AbstractFitengine:
2121                ## But this should be corrected later.
2122                else:
2123                    dy = weight#deepcopy(data_copy.dy)
2124                dy[dy==0] = 1 
2125            fn = data_copy.y[index] 
2126            theory_data = self.page_finder[page_id].get_theory_data(fid=data_copy.id)
2127            gn = theory_data.y
2128            en = dy[index]
2129            # build residuals
2130            residuals = Data1D()
2131            residuals.y = (fn - gn) / en
2132            residuals.x = data_copy.x[index]
2133            residuals.dy = numpy.ones(len(residuals.y))
2134            residuals.dx = None
2135            residuals.dxl = None
2136            residuals.dxw = None
2137            residuals.ytransform = 'y'
2138            # For latter scale changes
2139            residuals.xaxis('\\rm{Q} ', 'A^{-1}')
2140            residuals.yaxis('\\rm{Residuals} ', 'normalized')
2141        new_plot = residuals
2142        new_plot.name = "Residuals for " + str(theory_data.name.split()[0]) +"[" +  str(data.name) +"]"
2143        ## allow to highlight data when plotted
2144        new_plot.interactive = True
2145        ## when 2 data have the same id override the 1 st plotted
2146        new_plot.id = "res" + str(data_copy.id)#name + " residuals"
2147        ##group_id specify on which panel to plot this data
2148        group_id = self.page_finder[page_id].get_graph_id()
2149        if group_id == None:
2150            group_id = data.group_id
2151        new_plot.group_id ="res" + str(group_id)
2152        #new_plot.is_data = True
2153        ##post data to plot
2154        title = new_plot.name
2155        self.page_finder[page_id].set_residuals(residuals=new_plot, fid=data.id)
2156        self.parent.update_theory(data_id=data.id, theory=new_plot)
2157        batch_on = self.fit_panel.get_page_by_id(page_id).batch_on
2158        if not batch_on:
2159            wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot, title=title))
2160     
2161       
2162#def profile(fn, *args, **kw):
2163#    import cProfile, pstats, os
2164#    global call_result
2165#    def call():
2166#        global call_result
2167#        call_result = fn(*args, **kw)
2168#    cProfile.runctx('call()', dict(call=call), {}, 'profile.out')
2169#    stats = pstats.Stats('profile.out')
2170#    #stats.sort_stats('time')
2171#    stats.sort_stats('calls')
2172#    stats.print_stats()
2173#    os.unlink('profile.out')
2174#    return call_result
2175if __name__ == "__main__":
2176    i = Plugin()
2177   
2178   
2179   
2180   
Note: See TracBrowser for help on using the repository browser.