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

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 d225e32 was b9dd680, checked in by Jae Cho <jhjcho@…>, 13 years ago

fixed a bug resetting model in all the pages on compiling a custom model

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