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

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

new: overwright default changes; sum:added name control

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