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

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

added 'delete custom model menu'

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