source: sasview/fittingview/src/sans/perspectives/fitting/fitting.py @ 162c778

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

print unmatched data len on the res cal

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