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

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 0aca693 was 1ac202f, checked in by Jae Cho <jhjcho@…>, 12 years ago

remove useless parameters displaying

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