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

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

disable the plot results for batch: consistent issue.

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