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

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

added reasonable custom 2d

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